[412185] Automatic saving of project/repo properties does not work 
https://bugs.eclipse.org/bugs/show_bug.cgi?id=412185
diff --git a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPProjectImpl.java b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPProjectImpl.java
index 3ba80e3..e18867e 100644
--- a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPProjectImpl.java
+++ b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPProjectImpl.java
@@ -52,6 +52,7 @@
 import java.util.HashSet;

 import java.util.Iterator;

 import java.util.List;

+import java.util.Map.Entry;

 import java.util.Set;

 

 /**

@@ -327,11 +328,6 @@
 	}

 

 	/** {@inheritDoc} */

-	public ECPContainer getContext() {

-		return this;

-	}

-

-	/** {@inheritDoc} */

 	public boolean canDelete() {

 		return true;

 	}

@@ -601,7 +597,13 @@
 		return project;

 	}

 

-	/** {@inheritDoc} */

+	@Override

+	protected void propertiesChanged(Collection<Entry<String, String>> oldProperties,

+		Collection<Entry<String, String>> newProperties) {

+		ECPProjectManagerImpl.INSTANCE.storeElement(this);

+	}

+

+	@Deprecated

 	public void saveProperties() {

 		ECPProjectManagerImpl.INSTANCE.storeElement(this);

 	}

diff --git a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPProjectManagerImpl.java b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPProjectManagerImpl.java
index e6c2c90..2481eaf 100644
--- a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPProjectManagerImpl.java
+++ b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPProjectManagerImpl.java
@@ -171,11 +171,6 @@
 		return (Collection) projects;

 	}

 

-	@Override

-	public void storeElement(InternalProject project) {

-		super.storeElement(project);

-	}

-

 	/**

 	 * This is called by projects to notify observers about project changes.

 	 * 

diff --git a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPRepositoryImpl.java b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPRepositoryImpl.java
index 4c65222..3dc6d0c 100644
--- a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPRepositoryImpl.java
+++ b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPRepositoryImpl.java
@@ -13,6 +13,8 @@
 

 package org.eclipse.emf.ecp.internal.core;

 

+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;

+

 import org.eclipse.emf.ecp.core.ECPProject;

 import org.eclipse.emf.ecp.core.ECPProvider;

 import org.eclipse.emf.ecp.core.ECPRepository;

@@ -24,10 +26,10 @@
 import org.eclipse.emf.ecp.spi.core.InternalProject;

 import org.eclipse.emf.ecp.spi.core.InternalProvider;

 import org.eclipse.emf.ecp.spi.core.InternalProvider.LifecycleEvent;

+import org.eclipse.emf.ecp.spi.core.InternalRepository;

 import org.eclipse.emf.ecp.spi.core.util.DisposeException;

 import org.eclipse.emf.ecp.spi.core.util.ECPDisposable;

 import org.eclipse.emf.ecp.spi.core.util.ECPDisposable.DisposeListener;

-import org.eclipse.emf.ecp.spi.core.InternalRepository;

 

 import org.eclipse.core.runtime.Platform;

 

@@ -38,6 +40,7 @@
 import java.util.Collection;

 import java.util.Collections;

 import java.util.List;

+import java.util.Map.Entry;

 

 /**

  * This Class describes a repository.

@@ -46,6 +49,7 @@
  * @author Eugen Neufeld

  */

 public final class ECPRepositoryImpl extends PropertiesElement implements InternalRepository, DisposeListener {

+	@ExcludeFromDump

 	private final Disposable disposable = new Disposable(this) {

 		@Override

 		protected void doDispose() {

@@ -262,4 +266,10 @@
 		// TODO Consider to cache the result

 		return result.toArray(new InternalProject[result.size()]);

 	}

+

+	@Override

+	protected void propertiesChanged(Collection<Entry<String, String>> oldProperties,

+		Collection<Entry<String, String>> newProperties) {

+		ECPRepositoryManagerImpl.INSTANCE.storeElement(this);

+	}

 }

diff --git a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPRepositoryManagerImpl.java b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPRepositoryManagerImpl.java
index 6b8ca44..1372407 100644
--- a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPRepositoryManagerImpl.java
+++ b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/ECPRepositoryManagerImpl.java
@@ -14,6 +14,7 @@
 package org.eclipse.emf.ecp.internal.core;

 

 import org.eclipse.net4j.util.AdapterUtil;

+import org.eclipse.net4j.util.io.IOUtil;

 

 import org.eclipse.emf.ecp.core.ECPProvider;

 import org.eclipse.emf.ecp.core.ECPRepository;

@@ -23,12 +24,14 @@
 import org.eclipse.emf.ecp.core.util.ECPRepositoryAware;

 import org.eclipse.emf.ecp.core.util.ECPUtil;

 import org.eclipse.emf.ecp.core.util.observer.ECPObserver;

+import org.eclipse.emf.ecp.core.util.observer.ECPPropertiesObserver;

 import org.eclipse.emf.ecp.core.util.observer.ECPProvidersChangedObserver;

 import org.eclipse.emf.ecp.core.util.observer.ECPRepositoriesChangedObserver;

 import org.eclipse.emf.ecp.core.util.observer.ECPRepositoryContentChangedObserver;

 import org.eclipse.emf.ecp.internal.core.util.ExtensionParser;

 import org.eclipse.emf.ecp.internal.core.util.ExtensionParser.ExtensionDescriptor;

 import org.eclipse.emf.ecp.internal.core.util.InternalUtil;

+import org.eclipse.emf.ecp.internal.core.util.Properties;

 import org.eclipse.emf.ecp.internal.core.util.PropertiesStore;

 import org.eclipse.emf.ecp.spi.core.InternalProvider;

 import org.eclipse.emf.ecp.spi.core.InternalProvider.LifecycleEvent;

@@ -37,11 +40,19 @@
 import org.eclipse.core.runtime.IConfigurationElement;

 

 import java.io.File;

+import java.io.FileInputStream;

 import java.io.IOException;

+import java.io.InputStream;

 import java.io.ObjectInput;

+import java.io.ObjectInputStream;

 import java.io.ObjectOutput;

+import java.util.ArrayList;

 import java.util.Collection;

 import java.util.Collections;

+import java.util.HashSet;

+import java.util.List;

+import java.util.Map;

+import java.util.Map.Entry;

 import java.util.Set;

 

 /**

@@ -57,6 +68,11 @@
 	 */

 	public static ECPRepositoryManagerImpl INSTANCE;

 

+	/**

+	 * The file extension that is used for dynamic properties of statically declared repositories.

+	 */

+	private static final String DYNAMIC_PROPERTIES_EXTENSION = ".dynamic_properties";

+

 	private final RepositoryParser extensionParser = new RepositoryParser();

 

 	public ECPRepositoryManagerImpl() {

@@ -138,6 +154,19 @@
 	}

 

 	@Override

+	protected File getFile(InternalRepository element) {

+		if (element instanceof RepositoryDescriptor) {

+			return new File(getFolder(), element.getName() + DYNAMIC_PROPERTIES_EXTENSION);

+		}

+		return super.getFile(element);

+	}

+

+	@Override

+	protected boolean isLoadableElement(File file) {

+		return super.isLoadableElement(file) && !file.getName().endsWith(DYNAMIC_PROPERTIES_EXTENSION);

+	}

+

+	@Override

 	protected InternalRepository loadElement(ObjectInput in) throws IOException {

 		return new ECPRepositoryImpl(in);

 	}

@@ -156,8 +185,8 @@
 

 	@Override

 	protected void doActivate() throws Exception {

-		super.doActivate();

-		extensionParser.activate();

+		super.doActivate(); // 1. Load dynamic repositories

+		extensionParser.activate(); // 2. Register static repositories

 		ECPUtil.getECPObserverBus().register(this);

 	}

 

@@ -192,7 +221,38 @@
 	 */

 	private final class RepositoryDescriptor extends ExtensionDescriptor<InternalRepository> implements

 		InternalRepository {

-		private ECPProperties properties = ECPUtil.createProperties();

+		private Set<String> declaredPropertyKeys;

+		private ECPProperties properties = new Properties() {

+			@Override

+			public void addProperty(String key, String value) {

+				excludeDeclaredProperties(key);

+				super.addProperty(key, value);

+			}

+

+			@Override

+			public void removeProperty(String key) {

+				excludeDeclaredProperties(key);

+				super.removeProperty(key);

+			}

+

+			private void excludeDeclaredProperties(String key) {

+				if (declaredPropertyKeys != null && declaredPropertyKeys.contains(key)) {

+					throw new IllegalArgumentException("Statically declared property can not be changed: " + key);

+				}

+			}

+

+			@Override

+			protected Collection<Map.Entry<String, String>> getElementsToWrite() {

+				List<Map.Entry<String, String>> elementsToWrite = new ArrayList<Map.Entry<String, String>>();

+				for (Map.Entry<String, String> entry : getElements()) {

+					if (!declaredPropertyKeys.contains(entry.getKey())) {

+						elementsToWrite.add(entry);

+					}

+				}

+

+				return elementsToWrite;

+			}

+		};

 

 		public RepositoryDescriptor(String name, IConfigurationElement configurationElement) {

 			super(ECPRepositoryManagerImpl.this, name, TYPE, configurationElement);

@@ -201,16 +261,42 @@
 				String value = property.getAttribute("value");

 				properties.addProperty(key, value);

 			}

+

+			declaredPropertyKeys = new HashSet<String>(properties.getKeys());

+

+			InputStream stream = null;

+

+			try {

+				File file = getFile(this);

+				stream = new FileInputStream(file);

+				ObjectInputStream in = new ObjectInputStream(stream);

+

+				Properties dynamicProperties = new Properties(in);

+				for (Entry<String, String> property : dynamicProperties.getProperties()) {

+					properties.addProperty(property.getKey(), property.getValue());

+				}

+			} catch (IOException ex) {

+				Activator.log(ex);

+			} finally {

+				IOUtil.close(stream);

+			}

+

+			properties.addObserver(new ECPPropertiesObserver() {

+				public void propertiesChanged(ECPProperties properties,

+					Collection<Entry<String, String>> oldProperties, Collection<Entry<String, String>> newProperties) {

+					ECPRepositoryManagerImpl.INSTANCE.storeElement(RepositoryDescriptor.this);

+				}

+			});

 		}

 

 		/** {@inheritDoc} */

 		public boolean isStorable() {

-			return false;

+			return true;

 		}

 

 		/** {@inheritDoc} */

 		public void write(ObjectOutput out) throws IOException {

-			throw new UnsupportedOperationException();

+			((Properties) properties).write(out);

 		}

 

 		/** {@inheritDoc} */

diff --git a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/Properties.java b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/Properties.java
index 7607db8..3b426cc 100644
--- a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/Properties.java
+++ b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/Properties.java
@@ -44,7 +44,7 @@
 	}

 

 	public void write(ObjectOutput out) throws IOException {

-		Collection<Entry<String, String>> entries = getElements();

+		Collection<Entry<String, String>> entries = getElementsToWrite();

 		out.writeInt(entries.size());

 		for (Entry<String, String> entry : entries) {

 			out.writeUTF(entry.getKey());

@@ -53,13 +53,13 @@
 	}

 

 	/** {@inheritDoc} */

-	public final void addProperty(String key, String value) {

+	public void addProperty(String key, String value) {

 		Map.Entry<String, String> property = new Property(key, value);

 		doChangeElements(null, Collections.singleton(property));

 	}

 

 	/** {@inheritDoc} */

-	public final void removeProperty(String key) {

+	public void removeProperty(String key) {

 		doChangeElements(Collections.singleton(key), null);

 	}

 

@@ -99,6 +99,10 @@
 		return element.getKey();

 	}

 

+	protected Collection<Entry<String, String>> getElementsToWrite() {

+		return getElements();

+	}

+

 	/*

 	 * (non-Javadoc)

 	 * @see

diff --git a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/PropertiesElement.java b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/PropertiesElement.java
index da38b30..c7e7cb5 100644
--- a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/PropertiesElement.java
+++ b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/PropertiesElement.java
@@ -13,11 +13,14 @@
 

 import org.eclipse.emf.ecp.core.util.ECPProperties;

 import org.eclipse.emf.ecp.core.util.ECPPropertiesAware;

+import org.eclipse.emf.ecp.core.util.observer.ECPPropertiesObserver;

 import org.eclipse.emf.ecp.internal.core.util.PropertiesStore.StorableElement;

 

 import java.io.IOException;

 import java.io.ObjectInput;

 import java.io.ObjectOutput;

+import java.util.Collection;

+import java.util.Map.Entry;

 

 /**

  * @author Eike Stepper

@@ -28,11 +31,13 @@
 	public PropertiesElement(String name, ECPProperties properties) {

 		super(name);

 		this.properties = properties;

+		observeProperties();

 	}

 

 	public PropertiesElement(ObjectInput in) throws IOException {

 		super(in.readUTF());

 		properties = new Properties(in);

+		observeProperties();

 	}

 

 	/** {@inheritDoc} */

@@ -45,4 +50,18 @@
 	public final ECPProperties getProperties() {

 		return properties;

 	}

+

+	protected void propertiesChanged(Collection<Entry<String, String>> oldProperties,

+		Collection<Entry<String, String>> newProperties) {

+		// Do nothing

+	}

+

+	private void observeProperties() {

+		properties.addObserver(new ECPPropertiesObserver() {

+			public void propertiesChanged(ECPProperties properties, Collection<Entry<String, String>> oldProperties,

+				Collection<Entry<String, String>> newProperties) {

+				PropertiesElement.this.propertiesChanged(oldProperties, newProperties);

+			}

+		});

+	}

 }

diff --git a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/PropertiesStore.java b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/PropertiesStore.java
index 3a10dd6..6b0314f 100644
--- a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/PropertiesStore.java
+++ b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/internal/core/util/PropertiesStore.java
@@ -76,7 +76,7 @@
 		Set<ELEMENT> elements = new HashSet<ELEMENT>();

 		for (File file : folder.listFiles()) {

 			try {

-				if (file.isFile()) {

+				if (isLoadableElement(file)) {

 					InputStream stream = null;

 

 					try {

@@ -97,9 +97,7 @@
 							elements.add(element);

 						}

 					} finally {

-						if (stream != null) {

-							stream.close();

-						}

+						IOUtil.close(stream);

 					}

 				}

 			} catch (IOException ex) {

@@ -110,6 +108,10 @@
 		doChangeElements(null, elements);

 	}

 

+	protected boolean isLoadableElement(File file) {

+		return file.isFile();

+	}

+

 	protected abstract ELEMENT loadElement(ObjectInput in) throws IOException;

 

 	@Override

@@ -136,7 +138,7 @@
 		}

 	}

 

-	protected void storeElement(ELEMENT element) {

+	public void storeElement(ELEMENT element) {

 		File file = getFile(element);

 		File temp = new File(file.getParentFile(), file.getName() + ".tmp");

 		if (temp.isFile()) {

diff --git a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/spi/core/InternalProject.java b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/spi/core/InternalProject.java
index 564a652..ecbb7e8 100644
--- a/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/spi/core/InternalProject.java
+++ b/bundles/org.eclipse.emf.ecp.core/src/org/eclipse/emf/ecp/spi/core/InternalProject.java
@@ -92,7 +92,10 @@
 

 	/**

 	 * Saves the properties, such as visible packages or the name of the project into the workspace.

+	 * 

+	 * @deprecated As of 1.1 properties are saved automatically when they're changed.

 	 */

+	@Deprecated

 	void saveProperties();

 

 	/**