[sfs] Improve SFS performance by splitting SFS metadata into
multiple files

Change-Id: I848956831a887770018c156b024a05e407c41904
diff --git a/bundles/org.eclipse.core.resources.semantic/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.resources.semantic/META-INF/MANIFEST.MF
index 8d6537a..56c81ba 100644
--- a/bundles/org.eclipse.core.resources.semantic/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.core.resources.semantic/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.core.resources.semantic;singleton:=true
-Bundle-Version: 0.6.2.qualifier
+Bundle-Version: 0.6.3.qualifier
 Bundle-Activator: org.eclipse.core.internal.resources.semantic.SemanticResourcesPlugin
 Require-Bundle: org.eclipse.core.runtime;bundle-version="3.4.0",
  org.eclipse.core.resources;bundle-version="3.4.2",
@@ -15,7 +15,7 @@
 Bundle-Localization: plugin
 Export-Package: org.eclipse.core.internal.resources.semantic;x-friends:="org.eclipse.core.resources.semantic.test,org.eclipse.ui.resources.semantic",
  org.eclipse.core.internal.resources.semantic.cacheservice;x-friends:="org.eclipse.core.resources.semantic.test",
- org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB;x-internal:=true,
+ org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB;x-friends:="org.eclipse.core.resources.semantic.test",
  org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.impl;x-internal:=true,
  org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.util;x-internal:=true,
  org.eclipse.core.internal.resources.semantic.provider;x-friends:="org.eclipse.core.resources.semantic.test,,org.eclipse.ui.resources.semantic.examples",
diff --git a/bundles/org.eclipse.core.resources.semantic/SemanticDB.genmodel b/bundles/org.eclipse.core.resources.semantic/SemanticDB.genmodel
index 59c2613..c3d0363 100644
--- a/bundles/org.eclipse.core.resources.semantic/SemanticDB.genmodel
+++ b/bundles/org.eclipse.core.resources.semantic/SemanticDB.genmodel
@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<genmodel:GenModel xmi:version="2.0"
-    xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
-    xmlns:genmodel="http://www.eclipse.org/emf/2002/GenModel" copyrightText=""
-    modelDirectory="org.eclipse.core.resources.semantic/src" modelPluginID="org.eclipse.core.resources.semantic"
-    modelName="SemanticDB" nonNLSMarkers="true" importerID="org.eclipse.emf.importer.ecore"
-    complianceLevel="6.0" copyrightFields="false" language="" classNamePattern="">
+<genmodel:GenModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
+    xmlns:genmodel="http://www.eclipse.org/emf/2002/GenModel" copyrightText="" modelDirectory="org.eclipse.core.resources.semantic/src"
+    modelPluginID="org.eclipse.core.resources.semantic" modelName="SemanticDB" nonNLSMarkers="true"
+    importerID="org.eclipse.emf.importer.ecore" containmentProxies="true" complianceLevel="6.0"
+    copyrightFields="false" language="" classNamePattern="">
   <foreignModel>SemanticDB.ecore</foreignModel>
   <genPackages prefix="SemanticResourceDB" basePackage="org.eclipse.core.internal.resources.semantic.model"
       disposableProviderFactory="true" fileExtensions="" ecorePackage="SemanticDB.ecore#/">
@@ -27,6 +26,9 @@
       <genFeatures createChild="false" ecoreFeature="ecore:EAttribute SemanticDB.ecore#//ResourceTreeNode/type"/>
       <genFeatures createChild="false" ecoreFeature="ecore:EAttribute SemanticDB.ecore#//ResourceTreeNode/sessionProperties"/>
       <genFeatures createChild="false" ecoreFeature="ecore:EAttribute SemanticDB.ecore#//ResourceTreeNode/path"/>
+      <genFeatures createChild="false" ecoreFeature="ecore:EAttribute SemanticDB.ecore#//ResourceTreeNode/queryPart"/>
+      <genFeatures createChild="false" ecoreFeature="ecore:EAttribute SemanticDB.ecore#//ResourceTreeNode/remoteURI"/>
+      <genFeatures createChild="false" ecoreFeature="ecore:EAttribute SemanticDB.ecore#//ResourceTreeNode/dynamicContentProviderID"/>
     </genClasses>
     <genClasses ecoreClass="SemanticDB.ecore#//SemanticDB">
       <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference SemanticDB.ecore#//SemanticDB/roots"/>
diff --git a/bundles/org.eclipse.core.resources.semantic/plugin.properties b/bundles/org.eclipse.core.resources.semantic/plugin.properties
index d6d4e01..c0ceacc 100644
--- a/bundles/org.eclipse.core.resources.semantic/plugin.properties
+++ b/bundles/org.eclipse.core.resources.semantic/plugin.properties
@@ -18,4 +18,6 @@
 extension-point.name = Semantic Folder Template
 historyPageProviderExtPoint_XGRP = Semantic File System History Page
 contentProviderExtPoint_XGRP = Semantic File System Content Provider
-pathMappingExtPoint_XGRP = Path to Semantic File System Content Provider Mapping
\ No newline at end of file
+pathMappingExtPoint_XGRP = Path to Semantic File System Content Provider Mapping
+pluginName = Semantic File System Implementation
+providerName = www.eclipse.org
diff --git a/bundles/org.eclipse.core.resources.semantic/pom.xml b/bundles/org.eclipse.core.resources.semantic/pom.xml
index 53f9fbf..8dbe53d 100644
--- a/bundles/org.eclipse.core.resources.semantic/pom.xml
+++ b/bundles/org.eclipse.core.resources.semantic/pom.xml
@@ -9,7 +9,7 @@
 
   <groupId>org.eclipse.e4</groupId>
   <artifactId>org.eclipse.core.resources.semantic</artifactId>
-  <version>0.6.2-SNAPSHOT</version>
+  <version>0.6.3-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
 
 </project>
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticFileStore.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticFileStore.java
index b632b32..1148415 100644
--- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticFileStore.java
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticFileStore.java
@@ -710,7 +710,7 @@
 				this.fs.switchToExists(this.node, this.fs.getParentNode(this.node));
 				this.node.setType(TreeNodeType.FILE);
 				try {
-					this.fs.requestFlush(false);
+					this.fs.requestFlush(false, this.node);
 				} catch (CoreException e) {
 					Util.safeClose(os);
 					throw e;
@@ -836,8 +836,13 @@
 			checkAccessible();
 			((SemanticFileStore) targetParent).checkAccessible();
 
+			ResourceTreeNode currentParent = this.node.getParent();
+
 			this.node.setParent(((SemanticFileStore) targetParent).node);
 			this.node.setName(targetName);
+
+			this.fs.requestFlush(true, currentParent);
+			this.fs.requestFlush(true, ((SemanticFileStore) targetParent).node);
 		} finally {
 			this.fs.unlockForWrite();
 		}
@@ -1399,7 +1404,7 @@
 
 			createChildNode(name, true, null);
 
-			this.fs.requestFlush(false);
+			this.fs.requestFlush(false, this.node);
 		} finally {
 			this.fs.unlockForWrite();
 		}
@@ -1444,7 +1449,7 @@
 				// we don't use an empty map, but null
 				child.setPersistentProperties(null);
 			}
-			this.fs.requestFlush(false);
+			this.fs.requestFlush(false, this.node);
 
 		} finally {
 			this.fs.unlockForWrite();
@@ -1477,7 +1482,7 @@
 
 			createChildNode(name, false, null);
 
-			this.fs.requestFlush(false);
+			this.fs.requestFlush(false, this.node);
 		} finally {
 			this.fs.unlockForWrite();
 		}
@@ -1504,7 +1509,7 @@
 
 			createLocalChildNode(name, this.getPath().append(name), contentProviderID);
 
-			this.fs.requestFlush(false);
+			this.fs.requestFlush(false, this.node);
 		} finally {
 			this.fs.unlockForWrite();
 		}
@@ -1864,13 +1869,15 @@
 
 			SemanticFileStore.cleanupNodeAndChildren(node, getPath());
 
+			ResourceTreeNode topParent = this.fs.getHighestParent(this.node);
+
 			if (this.node instanceof TreeRoot) {
 				((TreeRoot) this.node).setParentDB(null);
 			} else {
 				this.node.setParent(null);
 			}
 
-			this.fs.requestFlush(false);
+			this.fs.requestFlush(false, topParent);
 
 			this.fs.requestURILocatorRebuild();
 		} finally {
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticFileSystem.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticFileSystem.java
index b9af0e2..c2b97d9 100644
--- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticFileSystem.java
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticFileSystem.java
@@ -25,9 +25,7 @@
 import org.eclipse.core.filesystem.IFileStore;
 import org.eclipse.core.filesystem.provider.FileSystem;
 import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.ResourceTreeNode;
-import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticDB;
 import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBFactory;
-import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBPackage;
 import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.TreeNodeType;
 import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.TreeRoot;
 import org.eclipse.core.internal.resources.semantic.util.ISemanticFileSystemLog;
@@ -46,11 +44,7 @@
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.common.util.TreeIterator;
 import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
-import org.eclipse.emf.ecore.resource.ResourceSet;
-import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
-import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
 
 /**
  * The Semantic File System.
@@ -69,11 +63,10 @@
 	private final Lock writeLock = this.rwl.writeLock();
 	private final ISemanticFileSystemLog log;
 
-	private SemanticDB db;
-	Resource metadataResource;
+	SemanticMetadataPersistenceManager mgr;
+	private File metadataFolder;
+
 	private SemanticURILocatorService uriLocator;
-	private boolean isDelayedFlush = false;
-	private boolean needsFlush = false;
 
 	/**
 	 * No-argument constructor
@@ -82,12 +75,19 @@
 
 		this.log = new SemanticFileSystemLog();
 
-		init();
+		init(null);
+	}
+
+	public SemanticFileSystem(File rootFolder) {
+
+		this.log = new SemanticFileSystemLog();
+
+		init(rootFolder);
 	}
 
 	public String[] getRootNames() throws CoreException {
 
-		if (this.db == null) {
+		if (this.mgr.getSemanticDB() == null) {
 			throw new SemanticResourceException(SemanticResourceStatusCode.SFS_DB_NOT_INITIALIZED, SemanticFileSystem.EMPTY,
 					Messages.SemanticFileSystem_NotInitialized_XMSG);
 		}
@@ -97,7 +97,7 @@
 		try {
 			lockForRead();
 
-			EList<TreeRoot> roots = this.db.getRoots();
+			EList<TreeRoot> roots = this.mgr.getSemanticDB().getRoots();
 
 			for (TreeRoot treeRoot : roots) {
 				result.add(treeRoot.getName());
@@ -116,12 +116,19 @@
 	 * @throws CoreException
 	 *             in case of failure
 	 */
-	public void requestFlush(boolean force) throws CoreException {
-		this.needsFlush = true;
+	public void requestFlush(boolean force, ResourceTreeNode node) throws CoreException {
+		node = getHighestParent(node);
 
-		if (!this.isDelayedFlush || force) {
-			saveSemanticDB();
+		saveSemanticDB(node.getName());
+	}
+
+	public ResourceTreeNode getHighestParent(ResourceTreeNode node) {
+		ResourceTreeNode parent = node.getParent();
+		while (parent != null) {
+			node = parent;
+			parent = node.getParent();
 		}
+		return node;
 	}
 
 	@Override
@@ -141,7 +148,7 @@
 			path = Path.EMPTY;
 		}
 
-		if (this.db == null) {
+		if (this.mgr.getSemanticDB() == null) {
 			return EFS.getNullFileSystem().getStore(path);
 		}
 
@@ -151,7 +158,7 @@
 			try {
 				lockForWrite();
 
-				EList<TreeRoot> roots = this.db.getRoots();
+				EList<TreeRoot> roots = this.mgr.getSemanticDB().getRoots();
 
 				TreeRoot treeRoot = null;
 
@@ -284,8 +291,8 @@
 			try {
 				lockForRead();
 
-				if (this.db != null) {
-					EList<TreeRoot> roots = this.db.getRoots();
+				if (this.mgr.getSemanticDB() != null) {
+					EList<TreeRoot> roots = this.mgr.getSemanticDB().getRoots();
 
 					for (TreeRoot treeRoot : roots) {
 						if (path.segment(0).equals(treeRoot.getName())) {
@@ -349,8 +356,8 @@
 			try {
 				lockForRead();
 
-				if (this.db != null) {
-					EList<TreeRoot> roots = this.db.getRoots();
+				if (this.mgr.getSemanticDB() != null) {
+					EList<TreeRoot> roots = this.mgr.getSemanticDB().getRoots();
 
 					for (TreeRoot treeRoot : roots) {
 						if (path.segment(0).equals(treeRoot.getName())) {
@@ -462,7 +469,7 @@
 	void switchToExists(ResourceTreeNode node, ResourceTreeNode parent) {
 		if (!node.isExists()) {
 			if (node instanceof TreeRoot) {
-				((TreeRoot) node).setParentDB(this.db);
+				((TreeRoot) node).setParentDB(this.mgr.getSemanticDB());
 			} else {
 
 				if (parent != null) {
@@ -517,59 +524,19 @@
 		return new SemanticFileStore(this, node);
 	}
 
-	private void init() {
-
-		File metadataFolder = SemanticResourcesPlugin.getCacheLocation().toFile();
-		metadataFolder.mkdirs();
-
-		File metadataFile = new File(metadataFolder, SemanticFileSystem.METADATA_FILENAME);
-
-		String metadataLocation = metadataFile.getAbsolutePath();
-
-		if (!metadataFile.exists()) {
-			initSemanticDB(metadataLocation);
-
+	private void init(File rootFolder) {
+		if (rootFolder != null) {
+			this.metadataFolder = rootFolder;
 		} else {
-			loadSemanticDB(metadataLocation);
+			this.metadataFolder = SemanticResourcesPlugin.getCacheLocation().toFile();
 		}
-	}
 
-	private void loadSemanticDB(String metadataLocation) {
+		mgr = new SemanticMetadataPersistenceManager(metadataFolder);
+
 		try {
-			SemanticResourceDBPackage pkg = SemanticResourceDBPackage.eINSTANCE;
-			pkg.eClass();
-
-			try {
-				this.lockForWrite();
-
-				ResourceSet resourceset = new ResourceSetImpl();
-
-				org.eclipse.emf.common.util.URI uri = org.eclipse.emf.common.util.URI.createFileURI(metadataLocation);
-
-				// Register the appropriate resource factory to handle all file
-				// extensions that would cover XMI as well.
-				resourceset.getResourceFactoryRegistry().getExtensionToFactoryMap().put(Resource.Factory.Registry.DEFAULT_EXTENSION,
-						new XMIResourceFactoryImpl());
-
-				this.metadataResource = resourceset.createResource(uri);
-
-				this.metadataResource.load(null);
-
-				EList<EObject> contents = this.metadataResource.getContents();
-
-				for (EObject eObject : contents) {
-					if (eObject instanceof SemanticDB) {
-						this.db = (SemanticDB) eObject;
-						this.migrateSemanticDB();
-						break;
-					}
-				}
-			} finally {
-				this.unlockForWrite();
-			}
-			this.needsFlush = false;
+			mgr.init();
 		} catch (IOException e) {
-			for (Diagnostic diagnostic : this.metadataResource.getErrors()) {
+			for (Diagnostic diagnostic : mgr.getMetadataResource().getErrors()) {
 				this.log.log(new SemanticResourceException(SemanticResourceStatusCode.SFS_INITIALIZATION_ERROR, SemanticFileSystem.EMPTY,
 						diagnostic.getMessage()));
 			}
@@ -579,62 +546,6 @@
 		}
 	}
 
-	private void migrateSemanticDB() {
-		TreeIterator<EObject> objects = this.db.eAllContents();
-		ArrayList<ResourceTreeNode> toBeRemoved = new ArrayList<ResourceTreeNode>();
-
-		try {
-			while (objects.hasNext()) {
-				EObject eObject = objects.next();
-				if (eObject instanceof TreeRoot) {
-					TreeRoot root = (TreeRoot) eObject;
-
-					if (!root.isExists()) {
-						toBeRemoved.add(root);
-					}
-				} else if (eObject instanceof ResourceTreeNode) {
-					ResourceTreeNode node = (ResourceTreeNode) eObject;
-
-					if (!node.isExists()) {
-						toBeRemoved.add(node);
-					}
-				}
-			}
-
-			for (ResourceTreeNode resourceTreeNode : toBeRemoved) {
-				if (resourceTreeNode instanceof TreeRoot) {
-					((TreeRoot) resourceTreeNode).setParentDB(null);
-				} else {
-					resourceTreeNode.setParent(null);
-				}
-			}
-		} catch (Throwable e) {
-			this.db = null;
-			this.log.log(new SemanticResourceException(SemanticResourceStatusCode.SFS_INITIALIZATION_ERROR, SemanticFileSystem.EMPTY,
-					Messages.SemanticFileSystem_SFSInitError_XMSG, e));
-		}
-
-	}
-
-	private void initSemanticDB(String metadataLocation) {
-		try {
-			this.lockForWrite();
-			ResourceSet resourceset = new ResourceSetImpl();
-
-			org.eclipse.emf.common.util.URI uri = org.eclipse.emf.common.util.URI.createFileURI(metadataLocation);
-
-			this.metadataResource = resourceset.createResource(uri);
-
-			this.db = SemanticResourceDBFactory.eINSTANCE.createSemanticDB();
-
-			this.metadataResource.getContents().add(this.db);
-
-			this.needsFlush = true;
-		} finally {
-			this.unlockForWrite();
-		}
-	}
-
 	private TreeRoot createRootNode(String name) {
 		TreeRoot root = SemanticResourceDBFactory.eINSTANCE.createTreeRoot();
 
@@ -664,7 +575,7 @@
 			if (parser.getShouldCreate()) {
 				root.setExists(true);
 				root.setPath(null);
-				root.setParentDB(this.db);
+				root.setParentDB(this.mgr.getSemanticDB());
 			}
 		}
 		return root;
@@ -676,19 +587,17 @@
 		node.setRemoteURI(parser.getURI());
 	}
 
-	private void saveSemanticDB() throws CoreException {
+	private void saveSemanticDB(String name) throws CoreException {
 		try {
-			if (this.needsFlush) {
-				if (SfsTraceLocation.CORE_DB.isActive()) {
-					SfsTraceLocation.getTrace().traceEntry(SfsTraceLocation.CORE_DB.getLocation());
-				}
-				try {
-					this.lockForWrite();
-					this.metadataResource.save(null);
-					this.needsFlush = false;
-				} finally {
-					this.unlockForWrite();
-				}
+			if (SfsTraceLocation.CORE_DB.isActive()) {
+				SfsTraceLocation.getTrace().traceEntry(SfsTraceLocation.CORE_DB.getLocation());
+			}
+			try {
+				this.lockForWrite();
+
+				this.mgr.saveSemanticDB(name);
+			} finally {
+				this.unlockForWrite();
 			}
 		} catch (IOException e) {
 			SemanticResourceException ex = new SemanticResourceException(SemanticResourceStatusCode.SFS_ERROR_WRITING_METADATA,
@@ -715,10 +624,6 @@
 	}
 
 	public String getPathToDb() {
-
-		File metadataFolder = SemanticResourcesPlugin.getCacheLocation().toFile();
-		metadataFolder.mkdirs();
-
 		File metadataFile = new File(metadataFolder, SemanticFileSystem.METADATA_FILENAME);
 
 		String metadataLocation = metadataFile.getAbsolutePath();
@@ -800,7 +705,7 @@
 		public void rebuildMapping(IProgressMonitor monitor) {
 			this.uri2pathMapping.clear();
 
-			TreeIterator<EObject> contents = this.fs.metadataResource.getAllContents();
+			TreeIterator<EObject> contents = this.fs.mgr.getMetadataResource().getAllContents();
 
 			while (contents.hasNext()) {
 				EObject eObject = contents.next();
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticMetadataPersistenceManager.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticMetadataPersistenceManager.java
new file mode 100644
index 0000000..7232043
--- /dev/null
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticMetadataPersistenceManager.java
@@ -0,0 +1,241 @@
+/*******************************************************************************
+ * Copyright (c) 2013 SAP AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eduard Bartsch (SAP AG) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.resources.semantic;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.ResourceTreeNode;
+import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticDB;
+import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBFactory;
+import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBPackage;
+import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.TreeRoot;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.TreeIterator;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
+
+public class SemanticMetadataPersistenceManager {
+
+	private static final String METADATA_FILENAME = "metadata.xmi"; //$NON-NLS-1$
+	final static IPath EMPTY = new Path(""); //$NON-NLS-1$
+
+	private SemanticDB db;
+
+	private Map<String, Resource> metadataResourceMap = new HashMap<String, Resource>();
+	private File metadataFolder;
+	Resource metadataResource;
+	ResourceSet metadataResourceSet = new ResourceSetImpl();
+
+	public SemanticMetadataPersistenceManager(File metadataFolder) {
+		this.metadataFolder = metadataFolder;
+	}
+
+	public SemanticDB getSemanticDB() {
+		return db;
+	}
+
+	public String getMetadataResourceLocation(String name) {
+		return getMetadataFile(name).getAbsolutePath();
+	}
+
+	public String getMetadataFileName(String name) {
+		return "$." + name + ".xmi"; //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	public void init() throws IOException {
+		// Register the appropriate resource factory to handle all file
+		// extensions that would cover XMI as well.
+		metadataResourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap()
+				.put(Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl());
+
+		metadataFolder.mkdirs();
+
+		File metadataFile = getMetadataFile(null);
+
+		if (!metadataFile.exists()) {
+			initSemanticDB();
+		} else {
+			loadSemanticDB();
+		}
+	}
+
+	public void initSemanticDB() {
+		this.metadataResource = metadataResourceSet.createResource(getResourceURI(METADATA_FILENAME));
+
+		this.db = SemanticResourceDBFactory.eINSTANCE.createSemanticDB();
+
+		this.metadataResource.getContents().add(this.db);
+	}
+
+	private void loadSemanticDB() throws IOException {
+		SemanticResourceDBPackage pkg = SemanticResourceDBPackage.eINSTANCE;
+		pkg.eClass();
+
+		this.metadataResource = metadataResourceSet.createResource(getResourceURI(METADATA_FILENAME));
+
+		this.metadataResource.load(null);
+
+		EList<EObject> contents = this.metadataResource.getContents();
+
+		for (EObject eObject : contents) {
+			if (eObject instanceof SemanticDB) {
+				this.db = (SemanticDB) eObject;
+				this.migrateSemanticDB();
+				break;
+			}
+		}
+	}
+
+	public void saveSemanticDB(String projectName) throws IOException {
+		if (SfsTraceLocation.CORE_DB.isActive()) {
+			SfsTraceLocation.getTrace().traceEntry(SfsTraceLocation.CORE_DB.getLocation());
+		}
+
+		// force assignment to separate resources
+		EList<TreeRoot> roots = this.db.getRoots();
+		for (TreeRoot treeRoot : roots) {
+			if (projectName == null || projectName.equals(treeRoot.getName())) {
+				assignToProjectSpecificMetadataResource(treeRoot);
+			}
+		}
+
+		this.metadataResource.save(null);
+
+		if (projectName == null) {
+			for (Entry<String, Resource> resourceEntry : this.metadataResourceMap.entrySet()) {
+				resourceEntry.getValue().save(null);
+			}
+		} else {
+			Resource resource = this.metadataResourceMap.get(projectName);
+
+			resource.save(null);
+		}
+	}
+
+	public File getMetadataFile(String name) {
+		String fileName;
+
+		if (name != null) {
+			fileName = getMetadataFileName(name);
+		} else {
+			fileName = METADATA_FILENAME;
+		}
+
+		return new File(metadataFolder, fileName);
+	}
+
+	public void migrateSemanticDB() throws IOException {
+		TreeIterator<EObject> objects = this.db.eAllContents();
+		ArrayList<ResourceTreeNode> toBeRemoved = new ArrayList<ResourceTreeNode>();
+		Set<String> filesToBeRemoved = listMetadataFiles();
+
+		while (objects.hasNext()) {
+			EObject eObject = objects.next();
+			if (eObject instanceof TreeRoot) {
+				TreeRoot root = (TreeRoot) eObject;
+
+				if (!root.isExists()) {
+					toBeRemoved.add(root);
+				} else {
+					assignToProjectSpecificMetadataResource(root);
+					filesToBeRemoved.remove(getMetadataFileName(root.getName()));
+				}
+			} else if (eObject instanceof ResourceTreeNode) {
+				ResourceTreeNode node = (ResourceTreeNode) eObject;
+
+				if (!node.isExists()) {
+					toBeRemoved.add(node);
+				}
+			}
+		}
+
+		for (ResourceTreeNode resourceTreeNode : toBeRemoved) {
+			if (resourceTreeNode instanceof TreeRoot) {
+				((TreeRoot) resourceTreeNode).setParentDB(null);
+				this.db.getRoots().remove(resourceTreeNode);
+			} else {
+				resourceTreeNode.setParent(null);
+			}
+		}
+
+		for (String fileName : filesToBeRemoved) {
+			File file = new File(this.metadataFolder, fileName);
+			if (file.exists()) {
+				file.delete();
+			}
+		}
+
+		this.metadataResource.save(null);
+
+		for (String name : this.metadataResourceMap.keySet()) {
+			File file = getMetadataFile(name);
+
+			if (!file.exists()) {
+				Resource resource = this.metadataResourceMap.get(name);
+
+				resource.save(null);
+			}
+		}
+	}
+
+	private Set<String> listMetadataFiles() {
+		File[] files = this.metadataFolder.listFiles(new FilenameFilter() {
+			public boolean accept(File dir, String name) {
+				return name.endsWith(".xmi") && !name.equals(METADATA_FILENAME); //$NON-NLS-1$
+			}
+		});
+
+		Set<String> names = new HashSet<String>();
+		for (File file : files) {
+			names.add(file.getName());
+		}
+		return names;
+	}
+
+	private void assignToProjectSpecificMetadataResource(TreeRoot root) {
+		Resource resource = this.metadataResourceMap.get(root.getName());
+
+		if (resource == null) {
+			org.eclipse.emf.common.util.URI uri = org.eclipse.emf.common.util.URI
+					.createFileURI(getMetadataResourceLocation(root.getName()));
+			resource = this.metadataResourceSet.createResource(uri);
+			this.metadataResourceMap.put(root.getName(), resource);
+		}
+
+		resource.getContents().add(root);
+	}
+
+	private org.eclipse.emf.common.util.URI getResourceURI(String fileName) {
+		File metadataFile = new File(metadataFolder, fileName);
+
+		String metadataLocation = metadataFile.getAbsolutePath();
+
+		return org.eclipse.emf.common.util.URI.createFileURI(metadataLocation);
+	}
+
+	public Resource getMetadataResource() {
+		return this.metadataResource;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticProperties.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticProperties.java
index fce7034..35cfa89 100644
--- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticProperties.java
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/SemanticProperties.java
@@ -154,7 +154,7 @@
 
 				this.notifyPersistentPropertySet(keyString, oldValue, value);
 
-				this.fs.requestFlush(false);
+				this.fs.requestFlush(false, this.node);
 			}
 		} finally {
 			this.fs.unlockForWrite();
@@ -216,7 +216,7 @@
 			if (needsFlush) {
 				this.node.setPersistentProperties(map);
 
-				this.fs.requestFlush(false);
+				this.fs.requestFlush(false, node);
 			}
 		} finally {
 			this.fs.unlockForWrite();
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/ResourceTreeNode.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/ResourceTreeNode.java
index eb2f622..9124a8f 100644
--- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/ResourceTreeNode.java
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/ResourceTreeNode.java
@@ -119,7 +119,7 @@
 	 * @return the value of the '<em>Children</em>' containment reference list.
 	 * @see org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBPackage#getResourceTreeNode_Children()
 	 * @see org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.ResourceTreeNode#getParent
-	 * @model opposite="parent" containment="true"
+	 * @model opposite="parent" containment="true" resolveProxies="true"
 	 * @generated
 	 */
 	EList<ResourceTreeNode> getChildren();
@@ -229,7 +229,8 @@
 	 * @return the value of the '<em>Persistent Properties</em>' attribute.
 	 * @see #setPersistentProperties(HashMap)
 	 * @see org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBPackage#getResourceTreeNode_PersistentProperties()
-	 * @model dataType="org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.PersistentProperties"
+	 * @model dataType=
+	 *        "org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.PersistentProperties"
 	 * @generated
 	 */
 	HashMap<String, String> getPersistentProperties();
@@ -323,7 +324,8 @@
 	 * @return the value of the '<em>Session Properties</em>' attribute.
 	 * @see #setSessionProperties(HashMap)
 	 * @see org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBPackage#getResourceTreeNode_SessionProperties()
-	 * @model dataType="org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SessionProperties"
+	 * @model dataType=
+	 *        "org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SessionProperties"
 	 *        transient="true"
 	 * @generated
 	 */
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/SemanticDB.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/SemanticDB.java
index 8227ef1..05ae0ea 100644
--- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/SemanticDB.java
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/SemanticDB.java
@@ -51,7 +51,7 @@
 	 * @return the value of the '<em>Roots</em>' containment reference list.
 	 * @see org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBPackage#getSemanticDB_Roots()
 	 * @see org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.TreeRoot#getParentDB
-	 * @model opposite="parentDB" containment="true"
+	 * @model opposite="parentDB" containment="true" resolveProxies="true"
 	 * @generated
 	 */
 	EList<TreeRoot> getRoots();
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/ResourceTreeNodeImpl.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/ResourceTreeNodeImpl.java
index 865947a..bcbbf64 100644
--- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/ResourceTreeNodeImpl.java
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/ResourceTreeNodeImpl.java
@@ -345,7 +345,7 @@
 	 */
 	public EList<ResourceTreeNode> getChildren() {
 		if (children == null) {
-			children = new EObjectContainmentWithInverseEList<ResourceTreeNode>(ResourceTreeNode.class, this,
+			children = new EObjectContainmentWithInverseEList.Resolving<ResourceTreeNode>(ResourceTreeNode.class, this,
 					SemanticResourceDBPackage.RESOURCE_TREE_NODE__CHILDREN, SemanticResourceDBPackage.RESOURCE_TREE_NODE__PARENT);
 		}
 		return children;
@@ -367,6 +367,17 @@
 	 * 
 	 * @generated
 	 */
+	public ResourceTreeNode basicGetParent() {
+		if (eContainerFeatureID() != SemanticResourceDBPackage.RESOURCE_TREE_NODE__PARENT)
+			return null;
+		return (ResourceTreeNode) eInternalContainer();
+	}
+
+	/**
+	 * <!-- begin-user-doc --> <!-- end-user-doc -->
+	 * 
+	 * @generated
+	 */
 	public NotificationChain basicSetParent(ResourceTreeNode newParent, NotificationChain msgs) {
 		msgs = eBasicSetContainer((InternalEObject) newParent, SemanticResourceDBPackage.RESOURCE_TREE_NODE__PARENT, msgs);
 		return msgs;
@@ -677,7 +688,9 @@
 			case SemanticResourceDBPackage.RESOURCE_TREE_NODE__CHILDREN :
 				return getChildren();
 			case SemanticResourceDBPackage.RESOURCE_TREE_NODE__PARENT :
-				return getParent();
+				if (resolve)
+					return getParent();
+				return basicGetParent();
 			case SemanticResourceDBPackage.RESOURCE_TREE_NODE__EXISTS :
 				return isExists();
 			case SemanticResourceDBPackage.RESOURCE_TREE_NODE__TEMPLATE_ID :
@@ -819,7 +832,7 @@
 			case SemanticResourceDBPackage.RESOURCE_TREE_NODE__CHILDREN :
 				return children != null && !children.isEmpty();
 			case SemanticResourceDBPackage.RESOURCE_TREE_NODE__PARENT :
-				return getParent() != null;
+				return basicGetParent() != null;
 			case SemanticResourceDBPackage.RESOURCE_TREE_NODE__EXISTS :
 				return exists != EXISTS_EDEFAULT;
 			case SemanticResourceDBPackage.RESOURCE_TREE_NODE__TEMPLATE_ID :
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticDBImpl.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticDBImpl.java
index 211106c..fe69588 100644
--- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticDBImpl.java
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticDBImpl.java
@@ -79,8 +79,8 @@
 	 */
 	public EList<TreeRoot> getRoots() {
 		if (roots == null) {
-			roots = new EObjectContainmentWithInverseEList<TreeRoot>(TreeRoot.class, this, SemanticResourceDBPackage.SEMANTIC_DB__ROOTS,
-					SemanticResourceDBPackage.TREE_ROOT__PARENT_DB);
+			roots = new EObjectContainmentWithInverseEList.Resolving<TreeRoot>(TreeRoot.class, this,
+					SemanticResourceDBPackage.SEMANTIC_DB__ROOTS, SemanticResourceDBPackage.TREE_ROOT__PARENT_DB);
 		}
 		return roots;
 	}
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticResourceDBFactoryImpl.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticResourceDBFactoryImpl.java
index e212379..dd4e1a6 100644
--- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticResourceDBFactoryImpl.java
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticResourceDBFactoryImpl.java
@@ -22,6 +22,7 @@
 import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBPackage;
 import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.TreeNodeType;
 import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.TreeRoot;
+import org.eclipse.core.runtime.QualifiedName;
 import org.eclipse.emf.ecore.EClass;
 import org.eclipse.emf.ecore.EDataType;
 import org.eclipse.emf.ecore.EObject;
@@ -180,8 +181,8 @@
 	 * @generated
 	 */
 	@SuppressWarnings("rawtypes")
-	public HashMap createPersistentPropertiesFromString(EDataType eDataType, String initialValue) {
-		return (HashMap) super.createFromString(initialValue);
+	public HashMap<String, String> createPersistentPropertiesFromString(EDataType eDataType, String initialValue) {
+		return (HashMap<String, String>) super.createFromString(initialValue);
 	}
 
 	/**
@@ -199,8 +200,8 @@
 	 * @generated
 	 */
 	@SuppressWarnings("rawtypes")
-	public HashMap createSessionPropertiesFromString(EDataType eDataType, String initialValue) {
-		return (HashMap) super.createFromString(initialValue);
+	public HashMap<QualifiedName, Object> createSessionPropertiesFromString(EDataType eDataType, String initialValue) {
+		return (HashMap<QualifiedName, Object>) super.createFromString(initialValue);
 	}
 
 	/**
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticResourceDBPackageImpl.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticResourceDBPackageImpl.java
index a735ff5..f413397 100644
--- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticResourceDBPackageImpl.java
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/SemanticResourceDBPackageImpl.java
@@ -446,12 +446,12 @@
 				getResourceTreeNode_Children(),
 				this.getResourceTreeNode(),
 				this.getResourceTreeNode_Parent(),
-				"children", null, 0, -1, ResourceTreeNode.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, IS_COMPOSITE, !IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); //$NON-NLS-1$
+				"children", null, 0, -1, ResourceTreeNode.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, IS_COMPOSITE, IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); //$NON-NLS-1$
 		initEReference(
 				getResourceTreeNode_Parent(),
 				this.getResourceTreeNode(),
 				this.getResourceTreeNode_Children(),
-				"parent", null, 0, 1, ResourceTreeNode.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_COMPOSITE, !IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); //$NON-NLS-1$
+				"parent", null, 0, 1, ResourceTreeNode.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_COMPOSITE, IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); //$NON-NLS-1$
 		initEAttribute(
 				getResourceTreeNode_Exists(),
 				ecorePackage.getEBoolean(),
@@ -498,14 +498,14 @@
 				getSemanticDB_Roots(),
 				this.getTreeRoot(),
 				this.getTreeRoot_ParentDB(),
-				"roots", null, 0, -1, SemanticDB.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, IS_COMPOSITE, !IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); //$NON-NLS-1$
+				"roots", null, 0, -1, SemanticDB.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, IS_COMPOSITE, IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); //$NON-NLS-1$
 
 		initEClass(treeRootEClass, TreeRoot.class, "TreeRoot", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS); //$NON-NLS-1$
 		initEReference(
 				getTreeRoot_ParentDB(),
 				this.getSemanticDB(),
 				this.getSemanticDB_Roots(),
-				"parentDB", null, 0, 1, TreeRoot.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_COMPOSITE, !IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); //$NON-NLS-1$
+				"parentDB", null, 0, 1, TreeRoot.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_COMPOSITE, IS_RESOLVE_PROXIES, !IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED); //$NON-NLS-1$
 		initEAttribute(
 				getTreeRoot_RootURI(),
 				ecorePackage.getEString(),
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/TreeRootImpl.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/TreeRootImpl.java
index d24c9b4..ff97dba 100644
--- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/TreeRootImpl.java
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/impl/TreeRootImpl.java
@@ -98,6 +98,17 @@
 	 * 
 	 * @generated
 	 */
+	public SemanticDB basicGetParentDB() {
+		if (eContainerFeatureID() != SemanticResourceDBPackage.TREE_ROOT__PARENT_DB)
+			return null;
+		return (SemanticDB) eInternalContainer();
+	}
+
+	/**
+	 * <!-- begin-user-doc --> <!-- end-user-doc -->
+	 * 
+	 * @generated
+	 */
 	public NotificationChain basicSetParentDB(SemanticDB newParentDB, NotificationChain msgs) {
 		msgs = eBasicSetContainer((InternalEObject) newParentDB, SemanticResourceDBPackage.TREE_ROOT__PARENT_DB, msgs);
 		return msgs;
@@ -200,7 +211,9 @@
 	public Object eGet(int featureID, boolean resolve, boolean coreType) {
 		switch (featureID) {
 			case SemanticResourceDBPackage.TREE_ROOT__PARENT_DB :
-				return getParentDB();
+				if (resolve)
+					return getParentDB();
+				return basicGetParentDB();
 			case SemanticResourceDBPackage.TREE_ROOT__ROOT_URI :
 				return getRootURI();
 		}
@@ -252,7 +265,7 @@
 	public boolean eIsSet(int featureID) {
 		switch (featureID) {
 			case SemanticResourceDBPackage.TREE_ROOT__PARENT_DB :
-				return getParentDB() != null;
+				return basicGetParentDB() != null;
 			case SemanticResourceDBPackage.TREE_ROOT__ROOT_URI :
 				return ROOT_URI_EDEFAULT == null ? rootURI != null : !ROOT_URI_EDEFAULT.equals(rootURI);
 		}
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/util/SemanticResourceDBSwitch.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/util/SemanticResourceDBSwitch.java
index ea1b8d5..ea2f444 100644
--- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/util/SemanticResourceDBSwitch.java
+++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/model/SemanticResourceDB/util/SemanticResourceDBSwitch.java
@@ -14,14 +14,13 @@
  */
 package org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.util;
 
-import java.util.List;
-
 import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.ResourceTreeNode;
 import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticDB;
 import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBPackage;
 import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.TreeRoot;
-import org.eclipse.emf.ecore.EClass;
 import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.util.Switch;
 
 /**
  * <!-- begin-user-doc --> The <b>Switch</b> for the model's inheritance
@@ -34,7 +33,7 @@
  * @see org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBPackage
  * @generated
  */
-public class SemanticResourceDBSwitch<T> {
+public class SemanticResourceDBSwitch<T> extends Switch<T> {
 	/**
 	 * The cached model package <!-- begin-user-doc --> <!-- end-user-doc -->
 	 * 
@@ -55,16 +54,16 @@
 	}
 
 	/**
-	 * Calls <code>caseXXX</code> for each class of the model until one returns
-	 * a non null result; it yields that result. <!-- begin-user-doc --> <!--
-	 * end-user-doc -->
+	 * Checks whether this is a switch for the given package. <!--
+	 * begin-user-doc --> <!-- end-user-doc -->
 	 * 
-	 * @return the first non-null result returned by a <code>caseXXX</code>
-	 *         call.
+	 * @parameter ePackage the package in question.
+	 * @return whether this is a switch for the given package.
 	 * @generated
 	 */
-	public T doSwitch(EObject theEObject) {
-		return doSwitch(theEObject.eClass(), theEObject);
+	@Override
+	protected boolean isSwitchFor(EPackage ePackage) {
+		return ePackage == modelPackage;
 	}
 
 	/**
@@ -76,24 +75,7 @@
 	 *         call.
 	 * @generated
 	 */
-	protected T doSwitch(EClass theEClass, EObject theEObject) {
-		if (theEClass.eContainer() == modelPackage) {
-			return doSwitch(theEClass.getClassifierID(), theEObject);
-		} else {
-			List<EClass> eSuperTypes = theEClass.getESuperTypes();
-			return eSuperTypes.isEmpty() ? defaultCase(theEObject) : doSwitch(eSuperTypes.get(0), theEObject);
-		}
-	}
-
-	/**
-	 * Calls <code>caseXXX</code> for each class of the model until one returns
-	 * a non null result; it yields that result. <!-- begin-user-doc --> <!--
-	 * end-user-doc -->
-	 * 
-	 * @return the first non-null result returned by a <code>caseXXX</code>
-	 *         call.
-	 * @generated
-	 */
+	@Override
 	protected T doSwitch(int classifierID, EObject theEObject) {
 		switch (classifierID) {
 			case SemanticResourceDBPackage.RESOURCE_TREE_NODE : {
@@ -188,6 +170,7 @@
 	 * @see #doSwitch(org.eclipse.emf.ecore.EObject)
 	 * @generated
 	 */
+	@Override
 	public T defaultCase(EObject object) {
 		return null;
 	}
diff --git a/examples/org.eclipse.core.resources.semantic.examples/META-INF/MANIFEST.MF b/examples/org.eclipse.core.resources.semantic.examples/META-INF/MANIFEST.MF
index 21cfc71..4353f65 100644
--- a/examples/org.eclipse.core.resources.semantic.examples/META-INF/MANIFEST.MF
+++ b/examples/org.eclipse.core.resources.semantic.examples/META-INF/MANIFEST.MF
@@ -21,14 +21,14 @@
  org.eclipse.core.resources.semantic.examples.remote,
  org.eclipse.core.resources.semantic.examples.webdav
 Import-Package: org.apache.http;version="4.1.4",
- org.apache.http.auth;version="4.1.3",
- org.apache.http.auth.params;version="4.1.3",
- org.apache.http.client;version="4.1.3",
- org.apache.http.client.entity;version="4.1.3",
- org.apache.http.client.methods;version="4.1.3",
- org.apache.http.client.params;version="4.1.3",
- org.apache.http.conn.params;version="4.1.3",
+ org.apache.http.auth;version="4.1.2",
+ org.apache.http.auth.params;version="4.1.2",
+ org.apache.http.client;version="4.1.2",
+ org.apache.http.client.entity;version="4.1.2",
+ org.apache.http.client.methods;version="4.1.2",
+ org.apache.http.client.params;version="4.1.2",
+ org.apache.http.conn.params;version="4.1.2",
  org.apache.http.entity;version="4.1.4",
- org.apache.http.impl.client;version="4.1.3",
+ org.apache.http.impl.client;version="4.1.2",
  org.apache.http.message;version="4.1.4",
  org.apache.http.params;version="4.1.4"
diff --git a/features/org.eclipse.e4.resources.feature/feature.xml b/features/org.eclipse.e4.resources.feature/feature.xml
index 62e12ec..694ba92 100644
--- a/features/org.eclipse.e4.resources.feature/feature.xml
+++ b/features/org.eclipse.e4.resources.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.e4.resources.feature"
       label="%featureName"
-      version="0.12.0.qualifier"
+      version="0.15.0.qualifier"
       provider-name="%providerName"
       image="eclipse_update_120.jpg">
 
diff --git a/features/org.eclipse.e4.resources.feature/pom.xml b/features/org.eclipse.e4.resources.feature/pom.xml
index 6670dd9..19433a8 100644
--- a/features/org.eclipse.e4.resources.feature/pom.xml
+++ b/features/org.eclipse.e4.resources.feature/pom.xml
@@ -9,7 +9,7 @@
 
   <groupId>org.eclipse.e4</groupId>
   <artifactId>org.eclipse.e4.resources.feature</artifactId>
-  <version>0.12.0-SNAPSHOT</version>
+  <version>0.15.0-SNAPSHOT</version>
   <packaging>eclipse-feature</packaging>
 
   <build>
diff --git a/tests/org.eclipse.core.resources.semantic.test/META-INF/MANIFEST.MF b/tests/org.eclipse.core.resources.semantic.test/META-INF/MANIFEST.MF
index 6a95e40..d2e741f 100644
--- a/tests/org.eclipse.core.resources.semantic.test/META-INF/MANIFEST.MF
+++ b/tests/org.eclipse.core.resources.semantic.test/META-INF/MANIFEST.MF
@@ -2,15 +2,16 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: Unit Tests for the Semantic File System
 Bundle-SymbolicName: org.eclipse.core.resources.semantic.test;singleton:=true
-Bundle-Version: 0.6.2.qualifier
+Bundle-Version: 0.6.3.qualifier
 Bundle-ClassPath: .
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Require-Bundle: org.eclipse.core.filesystem;bundle-version="1.2.0",
  org.eclipse.team.core;bundle-version="3.4.2",
  org.eclipse.core.resources;bundle-version="3.4.2",
  org.eclipse.core.runtime;bundle-version="3.4.0",
- org.eclipse.core.resources.semantic;bundle-version="0.6.2",
+ org.eclipse.core.resources.semantic;bundle-version="0.6.3",
  org.eclipse.jface;bundle-version="3.4.2",
+ org.eclipse.emf.ecore;bundle-version="2.4.2",
  org.eclipse.core.resources.semantic.examples;bundle-version="0.4.0"
 Export-Package: org.eclipse.core.resources.semantic.test,
  org.eclipse.core.resources.semantic.test.provider,
diff --git a/tests/org.eclipse.core.resources.semantic.test/pom.xml b/tests/org.eclipse.core.resources.semantic.test/pom.xml
index 70b7229..cc86ffa 100644
--- a/tests/org.eclipse.core.resources.semantic.test/pom.xml
+++ b/tests/org.eclipse.core.resources.semantic.test/pom.xml
@@ -9,7 +9,7 @@
 
   <groupId>org.eclipse.e4</groupId>
   <artifactId>org.eclipse.core.resources.semantic.test</artifactId>
-  <version>0.6.2-SNAPSHOT</version>
+  <version>0.6.3-SNAPSHOT</version>
   <packaging>eclipse-test-plugin</packaging>
 
 </project>
diff --git a/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/TestMetadataPersistenceManager.java b/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/TestMetadataPersistenceManager.java
new file mode 100644
index 0000000..957e84c
--- /dev/null
+++ b/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/TestMetadataPersistenceManager.java
@@ -0,0 +1,288 @@
+/*******************************************************************************
+ * Copyright (c) 2013 SAP AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eduard Bartsch (SAP AG) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.semantic.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.UUID;
+
+import org.eclipse.core.internal.resources.semantic.SemanticMetadataPersistenceManager;
+import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticDB;
+import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.SemanticResourceDBFactory;
+import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.TreeNodeType;
+import org.eclipse.core.internal.resources.semantic.model.SemanticResourceDB.TreeRoot;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class TestMetadataPersistenceManager {
+	private static final String TEST_ROOT = "testRoot";
+	private static final String TEST_ROOT2 = "testRoot2";
+	private File rootFolder;
+	private SemanticMetadataPersistenceManager mgr;
+	private SemanticMetadataPersistenceManager mgr2;
+	private SemanticMetadataPersistenceManager mgr3;
+
+	@Before
+	public void beforeMethod() throws Exception {
+		this.rootFolder = createTestRoot();
+		mgr = new SemanticMetadataPersistenceManager(this.rootFolder);
+		mgr2 = new SemanticMetadataPersistenceManager(this.rootFolder);
+		mgr3 = new SemanticMetadataPersistenceManager(this.rootFolder);
+	}
+
+	@After
+	public void afterMethod() throws Exception {
+		deleteRecursively(this.rootFolder);
+	}
+
+	@Test
+	public void test_GivenNoMetadataExists_WhenInitAndSave_ThenMetadataFileIsCreated() throws Exception {
+		mgr.init();
+
+		File metadataLocation = mgr.getMetadataFile(null);
+
+		Assert.assertTrue(rootFolder.exists());
+		Assert.assertFalse(metadataLocation.exists());
+
+		mgr.saveSemanticDB(null);
+
+		Assert.assertTrue("metadata file must be created", metadataLocation.exists());
+	}
+
+	@Test
+	public void test_GivenNoMetadataExists_WhenInitAddOneExistingRootAndSaveAll_ThenTwoMetadataFileAreCreated() throws Exception {
+		mgr.init();
+
+		SemanticDB db = mgr.getSemanticDB();
+
+		db.getRoots().add(createRootNode(TEST_ROOT, true));
+
+		mgr.saveSemanticDB(null);
+
+		Assert.assertTrue("metadata file must be created", mgr.getMetadataFile(null).exists());
+		Assert.assertTrue("metadata file must be created", mgr.getMetadataFile(TEST_ROOT).exists());
+	}
+
+	@Test
+	public void test_GivenNoMetadataExists_WhenInitAddTwoExistingRootsAndSaveAll_ThenThreeMetadataFileAreCreated() throws Exception {
+		mgr.init();
+
+		SemanticDB db = mgr.getSemanticDB();
+
+		db.getRoots().add(createRootNode(TEST_ROOT, true));
+
+		db.getRoots().add(createRootNode(TEST_ROOT2, true));
+
+		mgr.saveSemanticDB(null);
+
+		Assert.assertTrue("metadata file must be created", mgr.getMetadataFile(null).exists());
+		Assert.assertTrue("metadata file must be created", mgr.getMetadataFile(TEST_ROOT).exists());
+		Assert.assertTrue("metadata file must be created", mgr.getMetadataFile(TEST_ROOT2).exists());
+	}
+
+	@Test
+	public void test_GivenNoMetadataExists_WhenInitAddTwoExistingRootsAndSaveOne_ThenTwoMetadataFileAreCreated() throws Exception {
+		mgr.init();
+
+		SemanticDB db = mgr.getSemanticDB();
+
+		db.getRoots().add(createRootNode(TEST_ROOT, true));
+
+		db.getRoots().add(createRootNode(TEST_ROOT2, true));
+
+		mgr.saveSemanticDB(TEST_ROOT);
+
+		Assert.assertTrue("metadata file must be created", mgr.getMetadataFile(null).exists());
+		Assert.assertTrue("metadata file must be created", mgr.getMetadataFile(TEST_ROOT).exists());
+		Assert.assertFalse("metadata file must not be created", mgr.getMetadataFile(TEST_ROOT2).exists());
+	}
+
+	@Test
+	public void test_GivenOneRootExists_WhenInit_ThenOneRootIsPresent() throws Exception {
+		// given
+		mgr.init();
+		mgr.getSemanticDB().getRoots().add(createRootNode(TEST_ROOT, true));
+		mgr.saveSemanticDB(null);
+
+		// when
+		mgr2.init();
+
+		// then
+		SemanticDB db = mgr2.getSemanticDB();
+
+		Assert.assertEquals(1, db.getRoots().size());
+		Assert.assertEquals(TEST_ROOT, db.getRoots().get(0).getName());
+	}
+
+	@Test
+	public void test_GivenTwoRootsExists_WhenLoadAndSaveOnlyOneRoot_ThenOnlyTwoMetadataFilesAreChanged() throws Exception {
+		// given
+		mgr.init();
+		mgr.getSemanticDB().getRoots().add(createRootNode(TEST_ROOT, true));
+		mgr.getSemanticDB().getRoots().add(createRootNode(TEST_ROOT2, true));
+		mgr.saveSemanticDB(null);
+
+		long lastModification = mgr.getMetadataFile(null).lastModified();
+		long lastModification1 = mgr.getMetadataFile(TEST_ROOT).lastModified();
+		long lastModification2 = mgr.getMetadataFile(TEST_ROOT2).lastModified();
+
+		Thread.sleep(1011);
+
+		// when
+		mgr2.init();
+
+		mgr2.saveSemanticDB(TEST_ROOT);
+
+		// then
+		long newModification = mgr2.getMetadataFile(null).lastModified();
+		long newModification1 = mgr2.getMetadataFile(TEST_ROOT).lastModified();
+		long newModification2 = mgr2.getMetadataFile(TEST_ROOT2).lastModified();
+
+		Assert.assertTrue("metadata file must be modified", lastModification != newModification);
+		Assert.assertTrue("metadata file must be modified", lastModification1 != newModification1);
+		Assert.assertTrue("metadata file must not be modified", lastModification2 == newModification2);
+	}
+
+	@Test
+	public void test_GivenTwoRootsExistsInOldMetadata_WhenLoadAndMigrate_ThenThreeMetadataFilesAreCreated() throws Exception {
+		// given
+
+		// use mgr.init() to create a folder that simulates metadata
+		// content before migrations
+		mgr.init();
+
+		mgr2.initSemanticDB();
+		mgr2.getSemanticDB().getRoots().add(createRootNode(TEST_ROOT, true));
+		mgr2.getSemanticDB().getRoots().add(createRootNode(TEST_ROOT2, true));
+
+		Assert.assertFalse("metadata file must not be created", mgr2.getMetadataFile(null).exists());
+		Assert.assertFalse("metadata file must not be created", mgr2.getMetadataFile(TEST_ROOT).exists());
+		Assert.assertFalse("metadata file must not be created", mgr2.getMetadataFile(TEST_ROOT2).exists());
+
+		// when
+		mgr2.migrateSemanticDB();
+
+		// then
+		Assert.assertTrue("metadata file must be created", mgr2.getMetadataFile(null).exists());
+		Assert.assertTrue("metadata file must be created", mgr2.getMetadataFile(TEST_ROOT).exists());
+		Assert.assertTrue("metadata file must be created", mgr2.getMetadataFile(TEST_ROOT2).exists());
+	}
+
+	@Test
+	public void test_GivenOneRootExists_WhenSetRootNotExistsSaveAndLoad_ThenOneMetadataFileIsPresent() throws Exception {
+		// given
+		mgr.init();
+		mgr.getSemanticDB().getRoots().add(createRootNode(TEST_ROOT, true));
+		mgr.saveSemanticDB(null);
+
+		// when
+		SemanticDB db = mgr.getSemanticDB();
+
+		TreeRoot root = db.getRoots().get(0);
+		root.setExists(false);
+
+		mgr.saveSemanticDB(null);
+
+		mgr2.init();
+
+		// then
+		Assert.assertTrue("metadata file must be created", mgr.getMetadataFile(null).exists());
+		Assert.assertFalse("metadata file must not be created", mgr.getMetadataFile(TEST_ROOT).exists());
+	}
+
+	@Test
+	public void test_GivenOneRootExists_WhenRemoveRootSaveAndLoad_ThenOneMetadataFileIsPresent() throws Exception {
+		// given
+		mgr.init();
+		mgr.getSemanticDB().getRoots().add(createRootNode(TEST_ROOT, true));
+		mgr.saveSemanticDB(null);
+
+		// when
+		mgr.getSemanticDB().getRoots().remove(0);
+
+		mgr.saveSemanticDB(null);
+
+		mgr2.init();
+
+		// then
+		Assert.assertTrue("metadata file must be created", mgr.getMetadataFile(null).exists());
+		Assert.assertFalse("metadata file must not be created", mgr.getMetadataFile(TEST_ROOT).exists());
+	}
+
+	@Test
+	public void test_GivenTwoRootsExists_WhenRemoveRootSaveAndLoad_ThenOnlyTwoMetadataFilesArePresent() throws Exception {
+		// given
+		mgr.init();
+		mgr.getSemanticDB().getRoots().add(createRootNode(TEST_ROOT, true));
+		mgr.getSemanticDB().getRoots().add(createRootNode(TEST_ROOT2, true));
+		mgr.saveSemanticDB(null);
+
+		// when
+		mgr2.init();
+
+		mgr2.getSemanticDB().getRoots().remove(0);
+
+		mgr2.saveSemanticDB(null);
+
+		mgr3.init();
+
+		// then
+		Assert.assertTrue("metadata file must be created", mgr3.getMetadataFile(null).exists());
+		Assert.assertFalse("metadata file must not be created", mgr3.getMetadataFile(TEST_ROOT).exists());
+		Assert.assertTrue("metadata file must be created", mgr3.getMetadataFile(TEST_ROOT2).exists());
+
+	}
+
+	// Helper methods
+
+	private TreeRoot createRootNode(String name, boolean exists) {
+		TreeRoot root = SemanticResourceDBFactory.eINSTANCE.createTreeRoot();
+
+		root.setName(name);
+		root.setExists(exists);
+		root.setPath("/" + name); //$NON-NLS-1$
+		root.setType(TreeNodeType.PROJECT);
+
+		return root;
+	}
+
+	private File createTestRoot() {
+		String tmpdir = System.getProperty("java.io.tmpdir");
+		String uuid = UUID.randomUUID().toString();
+		File uniqueRoot = new File(tmpdir, uuid);
+		uniqueRoot.deleteOnExit();
+		return uniqueRoot;
+	}
+
+	private void deleteRecursively(File root) throws IOException {
+		if (root.exists()) {
+			if (root.isFile()) {
+				root.delete();
+			} else {
+				File[] files = root.listFiles();
+				if (files != null) {
+					for (File file : files) {
+						deleteRecursively(file);
+					}
+				}
+				if (!root.delete()) {
+					throw new IOException("Unable to delete " + root.getAbsolutePath());
+				}
+			}
+		}
+	}
+
+}
diff --git a/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/TestSemanticFileSystem.java b/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/TestSemanticFileSystem.java
new file mode 100644
index 0000000..c91ae67
--- /dev/null
+++ b/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/TestSemanticFileSystem.java
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * Copyright (c) 2013 SAP AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eduard Bartsch (SAP AG) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.resources.semantic.test;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.UUID;
+
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.internal.resources.semantic.SemanticFileSystem;
+import org.eclipse.core.resources.semantic.ISemanticFileSystem;
+import org.eclipse.core.resources.semantic.spi.ISemanticFileStore;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class TestSemanticFileSystem {
+	private static final String TEST_FOLDER2 = "testFolder2";
+	private static final String TEST2_PROJECT_XMI_FILENAME = "$.test2.xmi";
+	private static final String TEST_PROJECT_XMI_FILENAME = "$.test.xmi";
+	private static final String TEST_FOLDER = "testFolder";
+	private static final String TEST_ROOT2 = "test2";
+	private static final String TEST_ROOT = "test";
+	// private static final String TEST_ROOT = "testRoot";
+	// private static final String TEST_ROOT2 = "testRoot2";
+	private File rootFolder;
+	private SemanticFileSystem fs;
+
+	@Before
+	public void beforeMethod() throws Exception {
+		this.rootFolder = createTestRoot();
+	}
+
+	@After
+	public void afterMethod() throws Exception {
+		deleteRecursively(this.rootFolder);
+	}
+
+	@Test
+	public void test_GivenNoMetadataExists_WhenCreateSFSaddFolderAndSave_ThenMetadataFileIsCreated() throws Exception {
+
+		// when
+		fs = new SemanticFileSystem(this.rootFolder);
+
+		IFileStore store = fs.getStore(new URI(ISemanticFileSystem.SCHEME + ":/" + TEST_ROOT));
+
+		store.mkdir(0, null);
+
+		((ISemanticFileStore) store).addChildFolder(TEST_FOLDER);
+
+		// then
+		File metadataLocation = new File(fs.getPathToDb());
+		File testMetadataFile = new File(metadataLocation.getParentFile(), TEST_PROJECT_XMI_FILENAME);
+
+		assertTrue(rootFolder.exists());
+		assertTrue("metadata file must be created", metadataLocation.exists());
+		assertTrue("project-specific metadata file must be created", testMetadataFile.exists());
+	}
+
+	@Test
+	public void test_GivenNoMetadataExists_WhenCreateSFSaddFileAndSave_ThenMetadataFileIsCreated() throws Exception {
+
+		// when
+		fs = new SemanticFileSystem(this.rootFolder);
+
+		IFileStore store = fs.getStore(new URI(ISemanticFileSystem.SCHEME + ":/" + TEST_ROOT));
+
+		store.mkdir(0, null);
+
+		((ISemanticFileStore) store).addChildFile("testFile");
+
+		// then
+		File metadataLocation = new File(fs.getPathToDb());
+		File testMetadataFile = new File(metadataLocation.getParentFile(), TEST_PROJECT_XMI_FILENAME);
+
+		assertTrue(rootFolder.exists());
+		assertTrue("metadata file must be created", metadataLocation.exists());
+		assertTrue("project-specific metadata file must be created", testMetadataFile.exists());
+	}
+
+	@Test
+	public void test_GivenNoMetadataExists_WhenCreateSFSaddTwoNodesAndSave_ThenTwoMetadataFileAreCreated() throws Exception {
+
+		// when
+		fs = new SemanticFileSystem(this.rootFolder);
+
+		IFileStore store = fs.getStore(new URI(ISemanticFileSystem.SCHEME + ":/" + TEST_ROOT));
+
+		store.mkdir(0, null);
+
+		((ISemanticFileStore) store).addChildFolder(TEST_FOLDER);
+
+		store = fs.getStore(new URI(ISemanticFileSystem.SCHEME + ":/" + TEST_ROOT2));
+
+		store.mkdir(0, null);
+
+		((ISemanticFileStore) store).addChildFolder(TEST_FOLDER);
+
+		// then
+		File metadataLocation = new File(fs.getPathToDb());
+		File testMetadataFile = new File(metadataLocation.getParentFile(), TEST_PROJECT_XMI_FILENAME);
+		File test2MetadataFile = new File(metadataLocation.getParentFile(), TEST2_PROJECT_XMI_FILENAME);
+
+		assertTrue(rootFolder.exists());
+		assertTrue("metadata file must be created", metadataLocation.exists());
+		assertTrue("project-specific metadata file must be created", testMetadataFile.exists());
+		assertTrue("project-specific metadata file must be created", test2MetadataFile.exists());
+	}
+
+	@Test
+	public void test_GivenTwoProjectsExists_WhenLoadAndChangeOnlyOneProject_ThenOnlyTwoMetadataFilesAreChanged() throws Exception {
+		// given
+		fs = new SemanticFileSystem(this.rootFolder);
+
+		IFileStore store = fs.getStore(new URI(ISemanticFileSystem.SCHEME + ":/" + TEST_ROOT));
+
+		store.mkdir(0, null);
+
+		((ISemanticFileStore) store).addChildFolder(TEST_FOLDER);
+
+		store = fs.getStore(new URI(ISemanticFileSystem.SCHEME + ":/" + TEST_ROOT2));
+
+		store.mkdir(0, null);
+
+		((ISemanticFileStore) store).addChildFolder(TEST_FOLDER);
+
+		File metadataLocation = new File(fs.getPathToDb());
+		File testMetadataFile = new File(metadataLocation.getParentFile(), TEST_PROJECT_XMI_FILENAME);
+		File test2MetadataFile = new File(metadataLocation.getParentFile(), TEST2_PROJECT_XMI_FILENAME);
+
+		long lastModification = metadataLocation.lastModified();
+		long lastModification1 = testMetadataFile.lastModified();
+		long lastModification2 = test2MetadataFile.lastModified();
+
+		Thread.sleep(1011);
+
+		// when
+		fs = new SemanticFileSystem(this.rootFolder);
+
+		store = fs.getStore(new URI(ISemanticFileSystem.SCHEME + ":/" + TEST_ROOT));
+
+		((ISemanticFileStore) store).addChildFolder(TEST_FOLDER2);
+
+		// then
+		assertTrue("folder must exist", store.getChild(TEST_FOLDER).fetchInfo().exists());
+
+		long newModification = metadataLocation.lastModified();
+		long newModification1 = testMetadataFile.lastModified();
+		long newModification2 = test2MetadataFile.lastModified();
+
+		assertTrue("metadata file must be modified", lastModification != newModification);
+		assertTrue("metadata file must be modified", lastModification1 != newModification1);
+		assertTrue("metadata file must not be modified", lastModification2 == newModification2);
+
+		fs = new SemanticFileSystem(this.rootFolder);
+
+		store = fs.getStore(new URI(ISemanticFileSystem.SCHEME + ":/" + TEST_ROOT));
+
+		assertTrue("folder must exist", store.getChild(TEST_FOLDER2).fetchInfo().exists());
+	}
+
+	// Helper methods
+
+	private File createTestRoot() {
+		String tmpdir = System.getProperty("java.io.tmpdir");
+		String uuid = UUID.randomUUID().toString();
+		File uniqueRoot = new File(tmpdir, uuid);
+		uniqueRoot.deleteOnExit();
+		return uniqueRoot;
+	}
+
+	private void deleteRecursively(File root) throws IOException {
+		if (root.exists()) {
+			if (root.isFile()) {
+				root.delete();
+			} else {
+				File[] files = root.listFiles();
+				if (files != null) {
+					for (File file : files) {
+						deleteRecursively(file);
+					}
+				}
+				if (!root.delete()) {
+					throw new IOException("Unable to delete " + root.getAbsolutePath());
+				}
+			}
+		}
+	}
+
+}
diff --git a/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/suite/SfsTestSuite.java b/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/suite/SfsTestSuite.java
index 4a4f9a1..694c964 100644
--- a/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/suite/SfsTestSuite.java
+++ b/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/suite/SfsTestSuite.java
@@ -12,6 +12,8 @@
 package org.eclipse.core.resources.semantic.test.suite;
 
 import org.eclipse.core.resources.semantic.test.TestCacheService;
+import org.eclipse.core.resources.semantic.test.TestMetadataPersistenceManager;
+import org.eclipse.core.resources.semantic.test.TestSemanticFileSystem;
 import org.eclipse.core.resources.semantic.test.TestsCachingProvider;
 import org.eclipse.core.resources.semantic.test.TestsDefaultContentProvider;
 import org.eclipse.core.resources.semantic.test.TestsFederatingProvider;
@@ -33,6 +35,8 @@
 		TestsFederatingProvider2.class,//
 		TestsNullContentProvider.class,//
 		TestCacheService.class,//
+		TestMetadataPersistenceManager.class,//
+		TestSemanticFileSystem.class,//
 		TestsLinkedResources.class})
 public class SfsTestSuite {
 	// the suite