org.eclipse.core.internal.properties.PropertyBucket Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjtools Show documentation
Show all versions of aspectjtools Show documentation
AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during
compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based
@AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step.
This library is a superset of AspectJ weaver and hence also of AspectJ runtime.
/*******************************************************************************
* Copyright (c) 2004, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Lars Vogel - Bug 473427
*******************************************************************************/
package org.eclipse.core.internal.properties;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import org.eclipse.core.internal.localstore.Bucket;
import org.eclipse.core.internal.resources.ResourceException;
import org.eclipse.core.internal.utils.Messages;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.osgi.util.NLS;
public class PropertyBucket extends Bucket {
public static class PropertyEntry extends Entry {
private final static Comparator COMPARATOR = (o1, o2) -> {
int qualifierComparison = o1[0].compareTo(o2[0]);
return qualifierComparison != 0 ? qualifierComparison : o1[1].compareTo(o2[1]);
};
private static final String[][] EMPTY_DATA = new String[0][];
/**
* value is an array of qualified-key value pairs String[][] of
* {{propertyKey.qualifier, propertyKey.localName, propertyValue}}
*/
private String[][] value;
/**
* Deletes the property with the given name, and returns the result array. Returns the original
* array if the property to be deleted could not be found. Returns null
if the property was found
* and the original array had size 1 (instead of a zero-length array).
*/
static String[][] delete(String[][] existing, QualifiedName propertyName) {
// a size-1 array is a special case
if (existing.length == 1)
return (existing[0][0].equals(propertyName.getQualifier()) && existing[0][1].equals(propertyName.getLocalName())) ? null : existing;
// find the guy to delete
int deletePosition = search(existing, propertyName);
if (deletePosition < 0)
// not found, nothing to delete
return existing;
String[][] newValue = new String[existing.length - 1][];
if (deletePosition > 0)
// copy elements preceding the one to be removed
System.arraycopy(existing, 0, newValue, 0, deletePosition);
if (deletePosition < existing.length - 1)
// copy elements succeeding the one to be removed
System.arraycopy(existing, deletePosition + 1, newValue, deletePosition, newValue.length - deletePosition);
return newValue;
}
static String[][] insert(String[][] existing, QualifiedName propertyName, String propertyValue) {
// look for the right spot where to insert the new guy
int index = search(existing, propertyName);
if (index >= 0) {
// found existing occurrence - just replace the value
existing[index][2] = propertyValue;
return existing;
}
// not found - insert
int insertPosition = -index - 1;
String[][] newValue = new String[existing.length + 1][];
if (insertPosition > 0)
System.arraycopy(existing, 0, newValue, 0, insertPosition);
newValue[insertPosition] = new String[] {propertyName.getQualifier(), propertyName.getLocalName(), propertyValue};
if (insertPosition < existing.length)
System.arraycopy(existing, insertPosition, newValue, insertPosition + 1, existing.length - insertPosition);
return newValue;
}
/**
* Merges two entries (are always sorted). Duplicated additions replace existing ones.
*/
static Object merge(String[][] base, String[][] additions) {
int additionPointer = 0;
int basePointer = 0;
int added = 0;
String[][] result = new String[base.length + additions.length][];
while (basePointer < base.length && additionPointer < additions.length) {
int comparison = COMPARATOR.compare(base[basePointer], additions[additionPointer]);
if (comparison == 0) {
result[added++] = additions[additionPointer++];
// duplicate, override
basePointer++;
} else if (comparison < 0)
result[added++] = base[basePointer++];
else
result[added++] = additions[additionPointer++];
}
// copy the remaining states from either additions or base arrays
String[][] remaining = basePointer == base.length ? additions : base;
int remainingPointer = basePointer == base.length ? additionPointer : basePointer;
int remainingCount = remaining.length - remainingPointer;
System.arraycopy(remaining, remainingPointer, result, added, remainingCount);
added += remainingCount;
if (added == base.length + additions.length)
// no collisions
return result;
// there were collisions, need to compact
String[][] finalResult = new String[added][];
System.arraycopy(result, 0, finalResult, 0, finalResult.length);
return finalResult;
}
private static int search(String[][] existing, QualifiedName propertyName) {
return Arrays.binarySearch(existing, new String[] {propertyName.getQualifier(), propertyName.getLocalName(), null}, COMPARATOR);
}
public PropertyEntry(IPath path, PropertyEntry base) {
super(path);
//copy 2-dimensional array [x][y]
int xLen = base.value.length;
this.value = new String[xLen][];
for (int i = 0; i < xLen; i++) {
int yLen = base.value[i].length;
this.value[i] = new String[yLen];
System.arraycopy(base.value[i], 0, value[i], 0, yLen);
}
}
/**
* @param path a path to identify this property entry
* @param value an array of qualified-key value pairs
* (String[][] {{propertyKey.qualifier, propertyKey.localName, propertyValue}}
)
*/
protected PropertyEntry(IPath path, String[][] value) {
super(path);
this.value = value;
}
/**
* Compacts the data array removing any null slots. If non-null slots
* are found, the entry is marked for removal.
*/
private void compact() {
if (!isDirty())
return;
int occurrences = 0;
for (String[] s : value) {
if (s != null) {
value[occurrences++] = s;
}
}
if (occurrences == value.length)
// no states deleted
return;
if (occurrences == 0) {
// no states remaining
value = EMPTY_DATA;
delete();
return;
}
String[][] result = new String[occurrences][];
System.arraycopy(value, 0, result, 0, occurrences);
value = result;
}
@Override
public int getOccurrences() {
return value == null ? 0 : value.length;
}
public String getProperty(QualifiedName name) {
int index = search(value, name);
return index < 0 ? null : value[index][2];
}
public QualifiedName getPropertyName(int i) {
return new QualifiedName(this.value[i][0], this.value[i][1]);
}
public String getPropertyValue(int i) {
return this.value[i][2];
}
@Override
public Object getValue() {
return value;
}
@Override
public void visited() {
compact();
}
}
public static final byte INDEX = 1;
public static final byte QNAME = 2;
/** Version number for the current implementation file's format.
*
* Version 1:
*
* {@code
* FILE ::= VERSION_ID ENTRY+
* ENTRY ::= PATH PROPERTY_COUNT PROPERTY+
* PATH ::= string (does not contain project name)
* PROPERTY_COUNT ::= int
* PROPERTY ::= QUALIFIER LOCAL_NAME VALUE
* QUALIFIER ::= INDEX | QNAME
* INDEX -> byte int
* QNAME -> byte string
* UUID ::= byte[16]
* LAST_MODIFIED ::= byte[8]
* }
*/
private static final byte VERSION = 1;
private final List qualifierIndex = new ArrayList<>();
public PropertyBucket() {
super(!Boolean.getBoolean("org.eclipse.core.PropertyCache.disable")); //$NON-NLS-1$
}
@Override
protected Entry createEntry(IPath path, Object value) {
return new PropertyEntry(path, (String[][]) value);
}
private PropertyEntry getEntry(IPath path) {
String pathAsString = path.toString();
String[][] existing = (String[][]) getEntryValue(pathAsString);
if (existing == null)
return null;
return new PropertyEntry(path, existing);
}
@Override
protected String getIndexFileName() {
return "properties.index"; //$NON-NLS-1$
}
public String getProperty(IPath path, QualifiedName name) {
PropertyEntry entry = getEntry(path);
if (entry == null)
return null;
return entry.getProperty(name);
}
@Override
protected byte getVersion() {
return VERSION;
}
@Override
protected String getVersionFileName() {
return "properties.version"; //$NON-NLS-1$
}
@Override
public void load(String newProjectName, File baseLocation, boolean force) throws CoreException {
qualifierIndex.clear();
super.load(newProjectName, baseLocation, force);
}
@Override
protected Object readEntryValue(DataInputStream source) throws IOException, CoreException {
int length = source.readUnsignedShort();
String[][] properties = new String[length][3];
for (String[] propertie : properties) {
// qualifier
byte constant = source.readByte();
switch (constant) {
case QNAME:
propertie[0] = source.readUTF();
qualifierIndex.add(propertie[0]);
break;
case INDEX:
propertie[0] = qualifierIndex.get(source.readInt());
break;
default :
//if we get here the properties file is corrupt
IPath resourcePath = projectName == null ? IPath.ROOT : IPath.ROOT.append(projectName);
String msg = NLS.bind(Messages.properties_readProperties, resourcePath.toString());
throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, null, msg, null);
}
// localName
propertie[1] = source.readUTF();
// propertyValue
propertie[2] = source.readUTF();
}
return properties;
}
@Override
public void save() throws CoreException {
qualifierIndex.clear();
super.save();
}
public void setProperties(PropertyEntry entry) {
IPath path = entry.getPath();
String[][] additions = (String[][]) entry.getValue();
String pathAsString = path.toString();
String[][] existing = (String[][]) getEntryValue(pathAsString);
if (existing == null) {
setEntryValue(pathAsString, additions);
return;
}
setEntryValue(pathAsString, PropertyEntry.merge(existing, additions));
}
public void setProperty(IPath path, QualifiedName name, String value) {
String pathAsString = path.toString();
String[][] existing = (String[][]) getEntryValue(pathAsString);
if (existing == null) {
if (value != null)
setEntryValue(pathAsString, new String[][] {{name.getQualifier(), name.getLocalName(), value}});
return;
}
String[][] newValue;
if (value != null)
newValue = PropertyEntry.insert(existing, name, value);
else
newValue = PropertyEntry.delete(existing, name);
// even if newValue == existing we should mark as dirty (insert may just change the existing array)
setEntryValue(pathAsString, newValue);
}
@Override
protected void writeEntryValue(DataOutputStream destination, Object entryValue) throws IOException {
String[][] properties = (String[][]) entryValue;
destination.writeShort(properties.length);
for (String[] propertie : properties) {
// writes the property key qualifier
int index = qualifierIndex.indexOf(propertie[0]);
if (index == -1) {
destination.writeByte(QNAME);
destination.writeUTF(propertie[0]);
qualifierIndex.add(propertie[0]);
} else {
destination.writeByte(INDEX);
destination.writeInt(index);
}
// then the local name
destination.writeUTF(propertie[1]);
// then the property value
destination.writeUTF(propertie[2]);
}
}
}