TextFileProperty or a normal editable form/list/table

Hi

I listened to J. Lehtinen in Gothenburg last week and now I’m eager to try Vaadin out. I hope you can point me in the right direction and perhaps show me how to start my project.

This is what I want to do:

  1. I have a textfile with properties. It looks like this:
    ftp.username=testuser
    ftp.password=!GoVaadin!
    ftp.host=127.0.0.1
    ftp.remoteFolder=/Dropzone/
    #ftp.localFolder=C:\test\
    database.host=127.0.0.1
    database.port=1517
    database.username=sa

  2. I want to be able to list and edit these properties in a webpage. I don’t know if the properties will be listed in a form/list/table or something similar. Help me choose wisely.

  3. There should be a checkbox to enable/disable a property by the side of ech property for an easy change.

  4. I have seen some examples on the forum and in the Book of Vaadin which I got that use POJO’s. This needs to be on the fly change after pressing a commit-button and not stored in memory. It should also be possible to add new properties.

  5. I have seen that there will be a TextFilePropoerty in 6.2.0-pre1 which was compiled last night. I tried to download it in Eclipse but got an error so I donwloaded it from the website. I tried to use it but when compiling the “Hello Vaadin user”-example I got a lot of exceptions. Can I use this TextFileProperty for my application? How can I upgrade a 6.1.5-project to 6.2.0-pre1 if so?

Could you please show me how to start programming this application. I think I need a push in the right direction and perhaps some “stub-code”.

As an extra thing if I succeed with the above code is that I would like to add Tabs (on the left or above) which will represent the first part of the properties. From above there would be a Tab called “ftp”. In that view there would properties (username=testuser, password=!GoVaadin!..) and so on.

I don’t want you to do all my code for me but just help me start so -I can move in the right direction. Would I set up a “Vaadin Project” and then make everything in a few Java-classes. Any Widgets in this?

Thank you for your input.

/Magnus

Was the questions too hard or what? Seems kind of odd that noone tries to answer…Maybe the crew isn’t interested…

Maybe too many questions in one post for anyone to answer at once ;) Sorry.

Here are some quick thoughts

Whether you choose to use Table or a Select for this, you still need to list the properties in a Container. As there aren’t too many of them, maybe you could just for loop them into an IndexedContainer.

Why? In table you could use generated column to add checkboxes.

Please explain. What is changed and what is stored in memory?

If you use IndexedContainer - adding Pojos should be as easy as container.addItem(propertyname);

TexfFieldProperty is included in 6.2. You should use that.

Still - even pre1 should have worked perfectly well.

Just use tabsheet or accordion. Generate editors for each “part” of the property file in different tabs.

As a general rule - I would use “keep it simple stupid” architecture for your application. Just read the property file in memory and forget about lazy loading containers and stuff. There is not that much data anyways.

(first a small disclaimer: I only read the first two points in your question :slight_smile: )

The text file format you describe is
java.util.Properties
serialization. Below is a “PropertiesItem” class that makes it easy to bind java.util.Properties objects directly into a Vaadin Form like this:

Properties props = new Properties();
props.setProperty("Name", "John Doe");
props.setProperty("Age(int)", "39");
props.setProperty("DOA(boolean)", "false");
props.setProperty("Arrived(date)", "");
props.setProperty("Notes", "");
item = new PropertiesItem(props);

f = new Form();
f.setItemDataSource(item);		
mainWindow.addComponent(f);

Attached also a screenshot of the Form generated based on this.

package com.example.tester;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Properties;

import com.vaadin.data.Item;
import com.vaadin.data.Property;

/**
 * This class implements Vaadin Item interface using java.util.Properties as
 * backend /storage.
 * 
 * @see java.util.Properties
 * @author Sami Ekblad
 */
public class PropertiesItem implements Item {

	private static final long serialVersionUID = 1L;
	public static final String TYPE_SUFFIX_DEFAULT = "";
	public static final String TYPE_SUFFIX_STRING = "(string)";
	public static final String TYPE_SUFFIX_INT = "(int)";
	public static final String TYPE_SUFFIX_BOOLEAN = "(boolean)";
	public static final String TYPE_SUFFIX_DATE = "(date)";

	/**
	 * Parse the type suffix from property name.
	 * 
	 * @param type
	 * @return Object
	 */
	public static String parseToTypeSuffix(String suffix) {

		if (suffix == null) {
			return TYPE_SUFFIX_DEFAULT;
		}

		if (suffix.endsWith(TYPE_SUFFIX_STRING)) {
			return TYPE_SUFFIX_STRING;
		}
		if (suffix.endsWith(TYPE_SUFFIX_DATE)) {
			return TYPE_SUFFIX_DATE;
		}
		if (suffix.endsWith(TYPE_SUFFIX_INT)) {
			return TYPE_SUFFIX_INT;
		}
		if (suffix.endsWith(TYPE_SUFFIX_BOOLEAN)) {
			return TYPE_SUFFIX_BOOLEAN;
		}

		return TYPE_SUFFIX_DEFAULT;
	}

	/**
	 * Convert string suffix to Java class type. Method suffixToType.
	 * 
	 * @param type
	 * @return Class
	 */
	public static Class suffixToType(String suffix) {
		suffix = parseToTypeSuffix(suffix);

		if (suffix.endsWith(TYPE_SUFFIX_STRING)) {
			return String.class;
		}
		if (suffix.endsWith(TYPE_SUFFIX_DATE)) {
			return Date.class;
		}
		if (suffix.endsWith(TYPE_SUFFIX_INT)) {
			return Integer.class;
		}
		if (suffix.endsWith(TYPE_SUFFIX_BOOLEAN)) {
			return Boolean.class;
		}

		// Default to string type
		return String.class;
	}

	/**
	 * Convert the Java type string suffix. Method suffixToType.
	 * 
	 * @param type
	 * @return Class
	 */
	public static String typeToSuffix(Class type) {

		if (type.equals(Date.class)) {
			return TYPE_SUFFIX_DATE;
		}
		if (type.equals(Integer.class)) {
			return TYPE_SUFFIX_INT;
		}
		if (type.equals(Boolean.class)) {
			return TYPE_SUFFIX_BOOLEAN;
		}

		return TYPE_SUFFIX_DEFAULT;
	}

	private File file;
	private Properties values = new Properties();
	private HashMap typedProperties = new LinkedHashMap();

	private Method getStringMethod = null;
	private Method setStringMethod = null;

	private Comparator propertyOrder;

	public PropertiesItem() {
		this(new Properties());
	}

	public PropertiesItem(File propertiesFile) {
		this();
		setFile(propertiesFile);
	}

	public PropertiesItem(Properties defaults, Comparator propertyOrder) {
		this(defaults);
		this.propertyOrder = propertyOrder;
	}

	public PropertiesItem(Properties defaults) {

		// Initlize methods
		Class c = Properties.class;
		try {
			getStringMethod = c.getDeclaredMethod("getProperty",
					new Class[] { String.class });
			setStringMethod = c.getDeclaredMethod("setProperty", new Class[] {
					String.class, String.class });
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}

		setProperties(defaults);

	}

	/** Set the properties storage */
	protected void setProperties(Properties defaults) {

		// Remove all
		removeAllProperties();

		// Create default properties as specified types
		if (defaults != null) {
			List propIds = new ArrayList(defaults.keySet());
			for (Iterator i = propIds.iterator(); i.hasNext();) {
				String id = (String) i.next();
				addItemPropertyByName(id).setValue(defaults.get(id));
			}
		}
	}

	/**
	 * Remove all properties.
	 */
	public void removeAllProperties() {
		values.clear();
		typedProperties.clear();
	}

	/**
	 * @see com.vaadin.data.Item#getItemProperty(java.lang.Object)
	 */
	public Property getItemProperty(Object id) {
		return (Property) typedProperties.get(id);
	}

	/**
	 * @see com.vaadin.data.Item#getItemPropertyIds()
	 */
	public Collection getItemPropertyIds() {
		List l = new ArrayList(typedProperties.keySet());
		if (propertyOrder != null) {
			// Use specified order
			Collections.sort(l, this.propertyOrder);
		} else {
			// Alphabetical order
			Collections.sort(l);
		}
		return l;
	}

	/**
	 * @see com.vaadin.data.Item#addItemProperty(java.lang.Object,
	 *      com.vaadin.data.Property)
	 */
	public boolean addItemProperty(Object id, Property property)
			throws UnsupportedOperationException {

		Class clazz = property.getType();
		String type = typeToSuffix(clazz);

		// Create and add the new property
		Property p = new PropertiesProperty(clazz, id.toString() + type, values);
		typedProperties.put(id, p);

		// Copy the value
		p.setValue(property.getValue());
		return true;
	}

	/**
	 * @see com.vaadin.data.Item#removeItemProperty(java.lang.Object)
	 */
	public boolean removeItemProperty(Object arg0)
			throws UnsupportedOperationException {
		return typedProperties.remove(arg0) != null;
	}

	/** Add item property of given id. Parses the type from id. */
	protected Property addItemPropertyByName(String id) {

		String type = parseToTypeSuffix(id);
		String name = id.substring(0, id.length() - type.length());
		Class clazz = suffixToType(type);

		Property p = new PropertiesProperty(clazz, id, values);
		typedProperties.put(name, p);

		return p;
	}

	public void setType(Object id, Class newType) {
		PropertiesProperty p = (PropertiesProperty) getItemProperty(id);
		if (p != null) {
			p.setType(newType);
		}
	}

	public class PropertiesProperty implements Property {

		private Class type = String.class;
		private Properties store;
		private boolean readOnly = false;
		private String key;
		private Constructor constructor = null;
		private SimpleDateFormat df;

		private PropertiesProperty(Class type, String key, Properties store) {
			this.key = key;
			this.store = store;
			setType(type);
			if (type.equals(Date.class)) {
				df = new SimpleDateFormat();
			}
		}

		/**
		 * @see com.vaadin.data.Property#getValue()
		 */
		public Object getValue() {
			try {
				if (type.equals(Date.class)) {
					String val = store.getProperty(key);
					if (val != null)
						return df.parse(val);
				} else {
					return constructor.newInstance(new Object[] { store
							.getProperty(key) });
				}
			} catch (Exception e) {
				try {
					return type.newInstance();
				} catch (Exception ex) {
					// Ignore
				}
			}
			return null;
		}

		/**
		 * @see com.vaadin.data.Property#setValue(java.lang.Object)
		 */
		public void setValue(Object newValue) throws ReadOnlyException,
				ConversionException {
			if (newValue == null)
				store.setProperty(key, "");
			else {
				if (newValue instanceof Date && type.equals(Date.class)) {
					store.setProperty(key, df.format(newValue));
				} else {
					store.setProperty(key, newValue.toString());
				}
			}
		}

		/**
		 * @see com.vaadin.data.Property#getType()
		 */
		public Class getType() {
			return type;
		}

		public void setType(Class newType) {
			this.type = newType;
			try {
				constructor = type.getConstructor(new Class[] { String.class });
			} catch (NoSuchMethodException e) {
				throw new IllegalArgumentException(
						"Unsupported type in PropertiesItem property '"
								+ this.key + "':" + newType);
			}
		}

		/**
		 * @see com.vaadin.data.Property#isReadOnly()
		 */
		public boolean isReadOnly() {
			return this.readOnly;
		}

		/**
		 * @see com.vaadin.data.Property#setReadOnly(boolean)
		 */
		public void setReadOnly(boolean newStatus) {
			this.readOnly = newStatus;
		}

		/**
		 * @see java.lang.Object#toString()
		 */
		public String toString() {
			return "" + getValue();
		}

	}

	/**
	 * Returns the file.
	 * 
	 * @return File
	 */
	public File getFile() {
		return file;
	}

	/**
	 * Sets the storage file.
	 * 
	 * @param file
	 *            The file to set
	 */
	public void setFile(File file) {
		this.file = file;
		if (file != null) {
			Properties defaults = new Properties();
			try {
				defaults.load(new FileInputStream(file));
				setProperties(defaults);
			} catch (FileNotFoundException e) {
			} catch (IOException e) {
			}
		}
	}

	public void save() {
		if (file == null)
			return;
		try {
			if (!this.file.exists())
				this.file.createNewFile();
			values.store(new FileOutputStream(this.file), "PropertiesItem");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Get the properties values.
	 * 
	 * @return Properies.
	 */
	public Properties getProperties() {
		return values;
	}

}

(For easier copy pasting the above code use:
http://uilder.virtuallypreinstalled.com/run/codecopy/
)

As you can see there is built-in typing for the properties using the type suffix in parentheses. Use it if you like - the default is String. Also for convenience there is setFile together with save function to persist the data.
11150.png

Hi

Thank you for your replies. It now seems like we will have the properties in a MSSQL so that will probably make this more standardized. Found a hibernate example which uses a editable table. But we might go for a Form instead because having 107 parameters on one row makes it kind hard to display. If we cant tilt the row so it becomes vertical instead it would maybe be doable. But we would also like to have a description next to it som making a form would be easier. But validation will be harder because we just can put in whatever we want in a field if its the wrong format.

Many Thanks,

/M

Sami,

Thanks for this nice little helper. An enhancement suggestion would be to have an option to auto-backup the existing file before saving the edited one. It’s a safe practice of course to make copy(s) of configuration files before editing them, an application for which this class is obviously very useful.

-Mark