/******************************************************************************* * Copyright (c) 2004, 2010 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.core.internal.properties; import java.io.File; import java.util.*; import org.eclipse.core.internal.localstore.Bucket; import org.eclipse.core.internal.localstore.BucketTree; import org.eclipse.core.internal.localstore.Bucket.Entry; import org.eclipse.core.internal.properties.PropertyBucket.PropertyEntry; import org.eclipse.core.internal.resources.*; import org.eclipse.core.internal.utils.Messages; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.runtime.*; import org.eclipse.osgi.util.NLS; /** * @see org.eclipse.core.internal.properties.IPropertyManager */ public class PropertyManager2 implements IPropertyManager { private static final int MAX_VALUE_SIZE = 2 * 1024; class PropertyCopyVisitor extends Bucket.Visitor { private List<PropertyEntry> changes = new ArrayList<PropertyEntry>(); private IPath destination; private IPath source; public PropertyCopyVisitor(IPath source, IPath destination) { this.source = source; this.destination = destination; } public void afterSaving(Bucket bucket) throws CoreException { saveChanges((PropertyBucket) bucket); changes.clear(); } private void saveChanges(PropertyBucket bucket) throws CoreException { if (changes.isEmpty()) return; // make effective all changes collected Iterator<PropertyEntry> i = changes.iterator(); PropertyEntry entry = i.next(); tree.loadBucketFor(entry.getPath()); bucket.setProperties(entry); while (i.hasNext()) bucket.setProperties(i.next()); bucket.save(); } public int visit(Entry entry) { PropertyEntry sourceEntry = (PropertyEntry) entry; IPath destinationPath = destination.append(sourceEntry.getPath().removeFirstSegments(source.segmentCount())); PropertyEntry destinationEntry = new PropertyEntry(destinationPath, sourceEntry); changes.add(destinationEntry); return CONTINUE; } } BucketTree tree; public PropertyManager2(Workspace workspace) { this.tree = new BucketTree(workspace, new PropertyBucket()); } public void closePropertyStore(IResource target) throws CoreException { // ensure any uncommitted are written to disk tree.getCurrent().save(); // flush in-memory state to avoid confusion if another project is later // created with the same name tree.getCurrent().flush(); } public synchronized void copy(IResource source, IResource destination, int depth) throws CoreException { copyProperties(source.getFullPath(), destination.getFullPath(), depth); } /** * Copies all properties from the source path to the target path, to the given depth. */ private void copyProperties(final IPath source, final IPath destination, int depth) throws CoreException { Assert.isLegal(source.segmentCount() > 0); Assert.isLegal(destination.segmentCount() > 0); Assert.isLegal(source.segmentCount() > 1 || destination.segmentCount() == 1); // copy history by visiting the source tree PropertyCopyVisitor copyVisitor = new PropertyCopyVisitor(source, destination); tree.accept(copyVisitor, source, BucketTree.DEPTH_INFINITE); } public synchronized void deleteProperties(IResource target, int depth) throws CoreException { tree.accept(new PropertyBucket.Visitor() { public int visit(Entry entry) { entry.delete(); return CONTINUE; } }, target.getFullPath(), depth == IResource.DEPTH_INFINITE ? BucketTree.DEPTH_INFINITE : depth); } public void deleteResource(IResource target) throws CoreException { deleteProperties(target, IResource.DEPTH_INFINITE); } public synchronized Map<QualifiedName, String> getProperties(IResource target) throws CoreException { final Map<QualifiedName, String> result = new HashMap<QualifiedName, String>(); tree.accept(new PropertyBucket.Visitor() { public int visit(Entry entry) { PropertyEntry propertyEntry = (PropertyEntry) entry; int propertyCount = propertyEntry.getOccurrences(); for (int i = 0; i < propertyCount; i++) result.put(propertyEntry.getPropertyName(i), propertyEntry.getPropertyValue(i)); return CONTINUE; } }, target.getFullPath(), BucketTree.DEPTH_ZERO); return result; } public synchronized String getProperty(IResource target, QualifiedName name) throws CoreException { if (name.getQualifier() == null) { String message = Messages.properties_qualifierIsNull; throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, target.getFullPath(), message, null); } IPath resourcePath = target.getFullPath(); PropertyBucket current = (PropertyBucket) tree.getCurrent(); tree.loadBucketFor(resourcePath); return current.getProperty(resourcePath, name); } public BucketTree getTree() { return tree; } public File getVersionFile() { return tree.getVersionFile(); } public synchronized void setProperty(IResource target, QualifiedName name, String value) throws CoreException { //resource may have been deleted concurrently //must check for existence within synchronized method Resource resource = (Resource) target; ResourceInfo info = resource.getResourceInfo(false, false); int flags = resource.getFlags(info); resource.checkAccessible(flags); // enforce the limit stated by the spec if (value != null && value.length() > MAX_VALUE_SIZE) { String message = NLS.bind(Messages.properties_valueTooLong, new Object[] {name.getQualifier(), name.getLocalName(), new Integer(MAX_VALUE_SIZE).toString()}); throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, target.getFullPath(), message, null); } if (name.getQualifier() == null) { String message = Messages.properties_qualifierIsNull; throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, target.getFullPath(), message, null); } IPath resourcePath = target.getFullPath(); tree.loadBucketFor(resourcePath); PropertyBucket current = (PropertyBucket) tree.getCurrent(); current.setProperty(resourcePath, name, value); current.save(); } public void shutdown(IProgressMonitor monitor) throws CoreException { tree.close(); } public void startup(IProgressMonitor monitor) { // nothing to do } }