blob: c2b97d92be4f9a0965353da0e901d36d6ba29146 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 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
* Mathias Kinzler (SAP AG) - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.resources.semantic;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.eclipse.core.filesystem.EFS;
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.SemanticResourceDBFactory;
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;
import org.eclipse.core.resources.semantic.ISemanticFileSystem;
import org.eclipse.core.resources.semantic.ISemanticURILocatorService;
import org.eclipse.core.resources.semantic.SemanticResourceException;
import org.eclipse.core.resources.semantic.SemanticResourceStatusCode;
import org.eclipse.core.resources.semantic.spi.ISemanticContentProvider;
import org.eclipse.core.resources.semantic.spi.ISemanticContentProviderFederation;
import org.eclipse.core.resources.semantic.spi.ISemanticFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
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.Diagnostic;
/**
* The Semantic File System.
* <p>
* In order to access specific methods, this may be cast to
* {@link ISemanticFileSystem}.
*
*/
public class SemanticFileSystem extends FileSystem implements ISemanticFileSystem {
private static final String METADATA_FILENAME = "metadata.xmi"; //$NON-NLS-1$
final static IPath EMPTY = new Path(""); //$NON-NLS-1$
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock readLock = this.rwl.readLock();
private final Lock writeLock = this.rwl.writeLock();
private final ISemanticFileSystemLog log;
SemanticMetadataPersistenceManager mgr;
private File metadataFolder;
private SemanticURILocatorService uriLocator;
/**
* No-argument constructor
*/
public SemanticFileSystem() {
this.log = new SemanticFileSystemLog();
init(null);
}
public SemanticFileSystem(File rootFolder) {
this.log = new SemanticFileSystemLog();
init(rootFolder);
}
public String[] getRootNames() throws CoreException {
if (this.mgr.getSemanticDB() == null) {
throw new SemanticResourceException(SemanticResourceStatusCode.SFS_DB_NOT_INITIALIZED, SemanticFileSystem.EMPTY,
Messages.SemanticFileSystem_NotInitialized_XMSG);
}
List<String> result = new ArrayList<String>();
try {
lockForRead();
EList<TreeRoot> roots = this.mgr.getSemanticDB().getRoots();
for (TreeRoot treeRoot : roots) {
result.add(treeRoot.getName());
}
} finally {
unlockForRead();
}
return result.toArray(new String[0]);
}
/**
* Requests a database update.
*
* @param force
* <code>true</code> for immediate update
* @throws CoreException
* in case of failure
*/
public void requestFlush(boolean force, ResourceTreeNode node) throws CoreException {
node = getHighestParent(node);
saveSemanticDB(node.getName());
}
public ResourceTreeNode getHighestParent(ResourceTreeNode node) {
ResourceTreeNode parent = node.getParent();
while (parent != null) {
node = parent;
parent = node.getParent();
}
return node;
}
@Override
public IFileStore getStore(URI uri) {
if (ISemanticFileSystem.SCHEME.equals(uri.getScheme())) {
return getFileStoreImplementation(uri);
}
return EFS.getNullFileSystem().getStore(uri);
}
IFileStore getFileStoreImplementation(String pathString, String queryString) {
IPath path;
if (pathString != null) {
path = new Path(null, pathString);
} else {
path = Path.EMPTY;
}
if (this.mgr.getSemanticDB() == null) {
return EFS.getNullFileSystem().getStore(path);
}
ISemanticFileStore store;
// we do never return the virtual root node ("semanticfs:/")
if (path.segmentCount() > 0) {
try {
lockForWrite();
EList<TreeRoot> roots = this.mgr.getSemanticDB().getRoots();
TreeRoot treeRoot = null;
for (TreeRoot root : roots) {
if (path.segment(0).equals(root.getName())) {
if (path.segmentCount() == 1) {
return getStore(root);
}
treeRoot = root;
break;
}
}
if (treeRoot == null) {
if (path.segmentCount() == 1) {
treeRoot = createRootNode(path.segment(0), queryString);
return getStore(treeRoot);
}
treeRoot = createRootNode(path.segment(0));
}
store = (ISemanticFileStore) getFileStoreRecursive(path, treeRoot, queryString);
} finally {
unlockForWrite();
}
if (!store.isExists() && queryString != null) {
SemanticQueryParser parser = new SemanticQueryParser(queryString);
if (parser.getShouldCreate()) {
try {
if (parser.getType().equals(TreeNodeType.FOLDER) || parser.getType().equals(TreeNodeType.PROJECT)) {
store.mkdir(EFS.NONE, new NullProgressMonitor());
} else if (parser.getType().equals(TreeNodeType.FILE)) {
ISemanticFileStore parent = (ISemanticFileStore) store.getParent();
parent.mkdir(EFS.NONE, new NullProgressMonitor());
((ISemanticFileStoreInternal) parent).addResource(path.lastSegment(), false, parser.getProviderID(), null,
new NullProgressMonitor());
ISemanticFileStoreInternal child = ((ISemanticFileStoreInternal) parent).getChildResource(path.lastSegment());
child.setRemoteURI(new URI(parser.getURI()), new NullProgressMonitor());
}
return store;
} catch (CoreException e) {
return EFS.getNullFileSystem().getStore(path);
} catch (URISyntaxException e) {
return EFS.getNullFileSystem().getStore(path);
}
}
}
return store;
}
return EFS.getNullFileSystem().getStore(path);
}
private IFileStore getFileStoreImplementation(URI uri) {
String pathString = uri.getPath();
String queryString = uri.getQuery();
return getFileStoreImplementation(pathString, queryString);
}
private IFileStore getFileStoreRecursive(IPath path, ResourceTreeNode rootNode, String queryString) {
String[] segments = path.segments();
ResourceTreeNode currentNode = rootNode;
boolean ready = true;
for (int i = 1; i < segments.length; i++) {
String name = segments[i];
EList<ResourceTreeNode> children = currentNode.getChildren();
boolean found = false;
for (ResourceTreeNode resourceTreeNode : children) {
if (resourceTreeNode.getName().equals(name)) {
currentNode = resourceTreeNode;
found = true;
break;
}
}
if (!found) {
ready = false;
break;
}
}
if (ready) {
return getStore(currentNode);
}
ResourceTreeNode newNode = createNonExistingNode(path);
if (queryString != null) {
SemanticQueryParser parser = new SemanticQueryParser(queryString);
applyQueryParameters(newNode, parser);
}
return getStore(newNode);
}
ResourceTreeNode getParentNode(ResourceTreeNode childNode) {
try {
this.lockForRead();
if (childNode.isExists()) {
return childNode.getParent();
}
if (childNode.getPath() != null) {
int idx = childNode.getPath().lastIndexOf("/"); //$NON-NLS-1$
if (idx >= 0) {
String parentPath = childNode.getPath().substring(0, idx);
return this.getNodeByPath(parentPath);
}
}
} finally {
this.unlockForRead();
}
return null;
}
public List<ResourceTreeNode> getNodesByPath(String pathString) {
ArrayList<ResourceTreeNode> nodes = new ArrayList<ResourceTreeNode>();
IPath path = new Path(null, pathString);
if (path.segmentCount() > 0) {
try {
lockForRead();
if (this.mgr.getSemanticDB() != null) {
EList<TreeRoot> roots = this.mgr.getSemanticDB().getRoots();
for (TreeRoot treeRoot : roots) {
if (path.segment(0).equals(treeRoot.getName())) {
nodes.add(treeRoot);
if (path.segmentCount() == 1) {
return nodes;
}
ResourceTreeNode currentNode = treeRoot;
boolean withinExistingTree = true;
for (int i = 1; i < path.segmentCount(); i++) {
String name = path.segment(i);
if (withinExistingTree) {
EList<ResourceTreeNode> children = currentNode.getChildren();
boolean found = false;
for (ResourceTreeNode resourceTreeNode : children) {
if (resourceTreeNode.getName().equals(name)) {
currentNode = resourceTreeNode;
found = true;
break;
}
}
if (!found) {
currentNode = createNonExistingNode(path.removeLastSegments(path.segmentCount() - i - 1));
withinExistingTree = false;
}
} else {
currentNode = createNonExistingNode(path.removeLastSegments(path.segmentCount() - i - 1));
}
nodes.add(currentNode);
}
return nodes;
}
}
}
// no root found, create new node hierarchy outside the tree
nodes.add(createRootNode(path.segment(0)));
for (int i = 1; i < path.segmentCount(); i++) {
nodes.add(createNonExistingNode(path.removeLastSegments(path.segmentCount() - i - 1)));
}
} finally {
unlockForRead();
}
}
return nodes;
}
ResourceTreeNode getNodeByPath(String pathString) {
IPath path = new Path(null, pathString);
// we do never return the virtual root node ("semanticfs:/")
if (path.segmentCount() > 0) {
try {
lockForRead();
if (this.mgr.getSemanticDB() != null) {
EList<TreeRoot> roots = this.mgr.getSemanticDB().getRoots();
for (TreeRoot treeRoot : roots) {
if (path.segment(0).equals(treeRoot.getName())) {
if (path.segmentCount() == 1) {
return treeRoot;
}
String[] segments = path.segments();
ResourceTreeNode currentNode = treeRoot;
boolean ready = true;
for (int i = 1; i < segments.length; i++) {
String name = segments[i];
EList<ResourceTreeNode> children = currentNode.getChildren();
boolean found = false;
for (ResourceTreeNode resourceTreeNode : children) {
if (resourceTreeNode.getName().equals(name)) {
currentNode = resourceTreeNode;
found = true;
break;
}
}
if (!found) {
ready = false;
break;
}
}
if (ready) {
return currentNode;
}
return createNonExistingNode(path);
}
}
}
} finally {
unlockForRead();
}
// no root found, create new node outside the tree
if (path.segmentCount() == 1) {
return createRootNode(path.segment(0));
}
return createNonExistingNode(path);
}
return null;
}
ISemanticContentProvider mkdir(ResourceTreeNode actNode, ResourceTreeNode parent) throws CoreException {
ISemanticContentProvider parentProvider = null;
if (parent != null && !parent.isExists()) {
ResourceTreeNode parentParent = this.getParentNode(parent);
parentProvider = mkdir(parent, parentParent);
} else if (parent != null) { // and exists
parentProvider = this.getStore(parent).getEffectiveContentProvider();
} else { // parent == null
parentProvider = this.getStore(actNode).getEffectiveContentProvider();
}
return makeFolder(actNode, parent, parentProvider);
}
ISemanticContentProvider makeFolder(ResourceTreeNode actNode, ResourceTreeNode parent, ISemanticContentProvider parentProvider)
throws CoreException {
ISemanticContentProvider effectiveProvider = parentProvider;
String federatedContentProviderId = null;
if (actNode.getType() == TreeNodeType.FILE) {
// wrong type encountered
throw new SemanticResourceException(SemanticResourceStatusCode.RESOURCE_WITH_OTHER_TYPE_EXISTS, this.getStore(actNode)
.getPath(), Messages.SemanticFileStore_MkDirOnFile_XMSG);
}
boolean bExistedBefore = actNode.isExists();
if (!actNode.isExists() && actNode.getTemplateID() == null) {
if (parentProvider instanceof ISemanticContentProviderFederation) {
federatedContentProviderId = ((ISemanticContentProviderFederation) parentProvider).getFederatedProviderIDForPath(new Path(
actNode.getPath()));
}
actNode.setTemplateID(federatedContentProviderId);
}
// TODO 0.2: this should be intercepted by the content provider to
// avoid unwanted folder creation
this.switchToExists(actNode, parent);
if (actNode.getType() != TreeNodeType.PROJECT) {
// set type folder if it is not already a project
actNode.setType(TreeNodeType.FOLDER);
}
if (actNode.getTemplateID() != null) {
ISemanticFileStore newStore = this.getStore(actNode);
effectiveProvider = newStore.getEffectiveContentProvider();
if (!bExistedBefore) {
effectiveProvider.onRootStoreCreate(newStore);
}
}
return effectiveProvider;
}
void switchToExists(ResourceTreeNode node, ResourceTreeNode parent) {
if (!node.isExists()) {
if (node instanceof TreeRoot) {
((TreeRoot) node).setParentDB(this.mgr.getSemanticDB());
} else {
if (parent != null) {
node.setParent(parent);
}
}
node.setPath(null);
node.setExists(true);
}
}
private ResourceTreeNode createNonExistingNode(IPath childPath) {
// all callers have a lock, so we don't lock here
ResourceTreeNode child = SemanticResourceDBFactory.eINSTANCE.createResourceTreeNode();
child.setName(childPath.lastSegment());
child.setType(TreeNodeType.UNKNOWN);
child.setExists(false);
child.setPath(childPath.toString());
child.setLocalOnly(true);
return child;
}
public IPath getPathForNode(ResourceTreeNode node) {
try {
lockForRead();
if (node.isExists()) {
StringBuilder sb = new StringBuilder(50);
sb.append('/');
sb.append(node.getName());
ResourceTreeNode parent = node.getParent();
while (parent != null) {
sb.insert(0, parent.getName());
sb.insert(0, '/');
parent = parent.getParent();
}
return new Path(sb.toString());
}
if (node.getPath() != null) {
return new Path(node.getPath());
}
return EMPTY;
} finally {
unlockForRead();
}
}
protected ISemanticFileStore getStore(ResourceTreeNode node) {
return new SemanticFileStore(this, node);
}
private void init(File rootFolder) {
if (rootFolder != null) {
this.metadataFolder = rootFolder;
} else {
this.metadataFolder = SemanticResourcesPlugin.getCacheLocation().toFile();
}
mgr = new SemanticMetadataPersistenceManager(metadataFolder);
try {
mgr.init();
} catch (IOException e) {
for (Diagnostic diagnostic : mgr.getMetadataResource().getErrors()) {
this.log.log(new SemanticResourceException(SemanticResourceStatusCode.SFS_INITIALIZATION_ERROR, SemanticFileSystem.EMPTY,
diagnostic.getMessage()));
}
this.log.log(new SemanticResourceException(SemanticResourceStatusCode.SFS_INITIALIZATION_ERROR, SemanticFileSystem.EMPTY,
Messages.SemanticFileSystem_SFSInitError_XMSG, e));
}
}
private TreeRoot createRootNode(String name) {
TreeRoot root = SemanticResourceDBFactory.eINSTANCE.createTreeRoot();
root.setName(name);
root.setExists(false);
root.setPath("/" + name); //$NON-NLS-1$
root.setType(TreeNodeType.PROJECT);
// root.setParentDB(this.db);
return root;
}
private TreeRoot createRootNode(String name, String queryString) {
TreeRoot root = SemanticResourceDBFactory.eINSTANCE.createTreeRoot();
root.setName(name);
root.setQueryPart(queryString);
root.setExists(false);
root.setPath("/" + name); //$NON-NLS-1$
root.setType(TreeNodeType.PROJECT);
if (queryString != null) {
SemanticQueryParser parser = new SemanticQueryParser(queryString);
applyQueryParameters(root, parser);
if (parser.getShouldCreate()) {
root.setExists(true);
root.setPath(null);
root.setParentDB(this.mgr.getSemanticDB());
}
}
return root;
}
private void applyQueryParameters(ResourceTreeNode node, SemanticQueryParser parser) {
node.setTemplateID(parser.getProviderID());
node.setType(parser.getType());
node.setRemoteURI(parser.getURI());
}
private void saveSemanticDB(String name) throws CoreException {
try {
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,
SemanticFileSystem.EMPTY, Messages.SemanticFileSystem_SFSUpdateError_XMSG, e);
throw ex;
}
}
protected void lockForRead() {
this.readLock.lock();
}
protected void unlockForRead() {
this.readLock.unlock();
}
protected void lockForWrite() {
this.writeLock.lock();
}
protected void unlockForWrite() {
this.writeLock.unlock();
}
public String getPathToDb() {
File metadataFile = new File(metadataFolder, SemanticFileSystem.METADATA_FILENAME);
String metadataLocation = metadataFile.getAbsolutePath();
return metadataLocation;
}
public ISemanticFileSystemLog getLog() {
return this.log;
}
static final class SemanticURILocatorService implements ISemanticURILocatorService {
private HashMap<String, ArrayList<IPath>> uri2pathMapping = new HashMap<String, ArrayList<IPath>>();
final SemanticFileSystem fs;
boolean needsRebuild;
/**
* @param fs
*
*/
public SemanticURILocatorService(SemanticFileSystem fs) {
this.fs = fs;
}
/**
* @throws CoreException
*/
public IPath[] locateURI(URI uri) throws CoreException {
try {
this.fs.lockForWrite();
if (this.needsRebuild) {
this.rebuildMapping(null);
}
ArrayList<IPath> paths = this.uri2pathMapping.get(uri.toString());
if (paths != null) {
return paths.toArray(new IPath[paths.size()]);
}
return new IPath[0];
} finally {
this.fs.unlockForWrite();
}
}
/**
* @throws CoreException
*/
public IPath[] locateURI(URI uri, IPath rootpath) throws CoreException {
try {
this.fs.lockForWrite();
if (this.needsRebuild) {
this.rebuildMapping(null);
}
ArrayList<IPath> paths = this.uri2pathMapping.get(uri.toString());
if (paths != null) {
ArrayList<IPath> filteredpaths = new ArrayList<IPath>();
for (IPath iPath : paths) {
if (rootpath.isPrefixOf(iPath)) {
filteredpaths.add(iPath);
}
}
return filteredpaths.toArray(new IPath[filteredpaths.size()]);
}
return new IPath[0];
} finally {
this.fs.unlockForWrite();
}
}
public IPath getPathForNode(ResourceTreeNode node) {
return this.fs.getPathForNode(node);
}
public void rebuildMapping(IProgressMonitor monitor) {
this.uri2pathMapping.clear();
TreeIterator<EObject> contents = this.fs.mgr.getMetadataResource().getAllContents();
while (contents.hasNext()) {
EObject eObject = contents.next();
if (eObject instanceof ResourceTreeNode) {
ResourceTreeNode node = (ResourceTreeNode) eObject;
String uriString = node.getRemoteURI();
if (uriString != null) {
addURI(getPathForNode(node), uriString);
}
}
}
this.needsRebuild = false;
}
public void addURI(IPath path, String uriString) {
ArrayList<IPath> paths = this.uri2pathMapping.get(uriString);
if (paths != null) {
paths.add(path);
} else {
paths = new ArrayList<IPath>();
paths.add(path);
this.uri2pathMapping.put(uriString, paths);
}
}
public void removeURI(IPath path, String uriString) {
ArrayList<IPath> paths = this.uri2pathMapping.get(uriString);
paths.remove(path);
if (paths.isEmpty()) {
this.uri2pathMapping.remove(uriString);
}
}
public void requestRebuild() {
this.needsRebuild = true;
}
}
/**
* @throws CoreException
*/
public ISemanticURILocatorService getURILocatorService(IProgressMonitor monitor) throws CoreException {
try {
this.lockForWrite();
if (this.uriLocator == null) {
this.uriLocator = new SemanticURILocatorService(this);
this.uriLocator.rebuildMapping(monitor);
}
return this.uriLocator;
} finally {
this.unlockForWrite();
}
}
/**
*
*/
public void requestURILocatorRebuild() {
if (this.uriLocator != null) {
this.uriLocator.requestRebuild();
}
}
public SemanticURILocatorService getURILocator() {
return this.uriLocator;
}
}