2

The problem here is that the property file we use has insanely huge name as the key and most of us run into incorrect key naming issues . so it got me thinking if there's a way to generate the following interface based on the property file. Every change we make to the property file will auto-adjust the Properties interface. Or is there other solution?

Property File

A=Apple
B=Bannana
C=Cherry

Should Generate The following Interface

interface Properties{
public static final String A = "A" // keys
public static final String B = "B"; 
public static final String C = "C"; 

}

So in my application code

String a_value = PROP.getString(Properties.A);
Njax3SmmM2x2a0Zf7Hpd
  • 1,354
  • 4
  • 22
  • 44
  • Seems like you'd be learning Maven. – Unihedron Aug 19 '14 at 07:37
  • 1
    Did you consider that changing your properties file will create compile errors in your application? I personally would generate the Properties interface once and then maintain it by hand. Maybe i would use enums instead of an interface. – Absurd-Mind Aug 19 '14 at 07:43

2 Answers2

1

There is an old rule about programming and not only about it, if something looks beautiful, then most probably it is the right way to do.

This approach does not look good, from my point of view.

The first thing:

Do not declare constants in interfaces. It violates the incapsulation approach. Check this article please: http://en.wikipedia.org/wiki/Constant_interface

The second thing:

Use a prefix for name part of your properties which are somehow special, let say: key_

And when you load your properties file, iterate over keys and extract keys with name that starts with key_ and use values of these keys as you planned to use those constants in your question.


UPDATE

Assume, we generate a huge properties file upon compilation process, using our Apache Ant script.

For example, let's properties file (myapp.properties) looks like that:

key_A = Apple
key_B = Banana
key_C = Cherry
anotherPropertyKey1 = blablabla1
anotherPropertyKey2 = blablabla2

our special properties which we want to handle have key names start with key_ prefix.

So, we write the following code (please note, it is not optimized, it is just proof of concept):

package propertiestest;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;


public class PropertiesTest {

  public static void main(String[] args) throws IOException {
       final String PROPERTIES_FILENAME = "myapp.properties";      

       SpecialPropertyKeysStore spkStore = 
                            new SpecialPropertyKeysStore(PROPERTIES_FILENAME);

       System.out.println(Arrays.toString(spkStore.getKeysArray()));

   }
}


class SpecialPropertyKeysStore {

    private final Set<String> keys;

    public SpecialPropertyKeysStore(String propertiesFileName) 
                                    throws FileNotFoundException, IOException {


        // prefix of name of a special property key
        final String KEY_PREFIX = "key_";        

    Properties propertiesHandler = new Properties();
        keys = new HashSet<>();

    try (InputStream input = new FileInputStream(propertiesFileName)) {

            propertiesHandler.load(input);

            Enumeration<?> enumeration = propertiesHandler.propertyNames();
            while (enumeration.hasMoreElements()) {
                String key = (String) enumeration.nextElement();
                if (key.startsWith(KEY_PREFIX)) {
                    keys.add(key);
                }
            }
    }        
    }

    public boolean isKeyPresent(String keyName) {
        return keys.contains(keyName);
    }

    public String[] getKeysArray() {
        String[] strTypeParam = new String[0];

        return keys.toArray(strTypeParam);
    }
}

Class SpecialPropertyKeysStore filters and collects all special keys into its instance.

And you can get an array of these keys, or check whether is key present or not.

If you run this code, you will get:

[key_C, key_B, key_A]

It is a string representation of returned array with special key names.

Change this code as you want to meet your requirements.

  • you can modify your properies file at compile time, for example - using Apache Ant. And in this case you don't need to change your code. Just modify properties file as you wish. Your code will extract all properties using the described approach. –  Aug 19 '14 at 08:38
  • answer not clear. what you mean iterate over keys and extract keys ? – Njax3SmmM2x2a0Zf7Hpd Aug 19 '14 at 08:42
  • in the `Properties` class, there is a method [propertyNames](http://docs.oracle.com/javase/8/docs/api/java/util/Properties.html#propertyNames--). Call it to get [`Enumeration`](http://docs.oracle.com/javase/8/docs/api/java/util/Enumeration.html) with property names. Walk over this Enumeration to get property names and process them. To check that a property name starts with `key_` (for example), use [String.startsWith](http://docs.oracle.com/javase/8/docs/api/java/lang/String.html#startsWith-java.lang.String-) method. –  Aug 19 '14 at 08:46
  • ohh that's the usual routine for getting property, isn't it?. In this case Properties key is insanely error prone. So wanted it to be generated to a interface field so we don't make any error's with hard-coding keys. – Njax3SmmM2x2a0Zf7Hpd Aug 19 '14 at 12:03
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/59584/discussion-between-njax3smmm2x2a0zf7hpd-and-rafael-osipov). – Njax3SmmM2x2a0Zf7Hpd Aug 19 '14 at 12:08
1

I would not generate a class or interface from properties because you would lose the abilities to :

  • document those properties, as they would be represented by a java element + javadocs
  • references those properties in your code, as they would be play old java constant, and the compiler would have full knowledge of them. Refactoring them would also be possible while it would not be possible with automatic names.

You can also use enums, or create some special Property class, with a name as only and final field. Then, you only need a get method that would take a Properties, a Map or whatever.

As for your request, you can execute code with the maven-exec-plugin.

You should simply create a main that would read your properties file, and for each keys:

  • convert the key to a valid java identifier (you can use isJavaIdentifierStart and isJavaIdentifierPart to replace invalid char by a _)
  • write your class/interface/whatever you like using plain old Java (and don't forget to escape for eventual doublequote or backslashes !)

Since it would be a part of your build, say before building other classes that would depends on those constants, I would recommend you to create a specific maven project to isolate those build.

Still, I would really don't do that and use a POJO loaded with whatever need (CDI, Spring, Static initialization, etc).

NoDataFound
  • 11,381
  • 33
  • 59