/*******************************************************************************
* Copyright (c) 2004, 2009 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.*;
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 changes= new ArrayList();
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 i= changes.iterator();
PropertyEntry entry= (PropertyEntry)i.next();
tree.loadBucketFor(entry.getPath());
bucket.setProperties(entry);
while (i.hasNext())
bucket.setProperties((PropertyEntry)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 getProperties(IResource target) throws CoreException {
final Map result= new HashMap();
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
}
}