| /******************************************************************************* |
| * Copyright (c) 2000, 2015 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 |
| * Oakland Software Incorporated - added getSessionProperties and getPersistentProperties |
| * James Blackburn (Broadcom Corp.) - ongoing development |
| * Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427 |
| *******************************************************************************/ |
| package org.eclipse.core.internal.resources; |
| |
| import java.io.*; |
| import java.util.Map; |
| import org.eclipse.core.internal.localstore.FileStoreRoot; |
| import org.eclipse.core.internal.utils.*; |
| import org.eclipse.core.internal.watson.IElementTreeData; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.QualifiedName; |
| |
| /** |
| * A data structure containing the in-memory state of a resource in the workspace. |
| */ |
| public class ResourceInfo implements IElementTreeData, ICoreConstants, IStringPoolParticipant { |
| protected static final int LOWER = 0xFFFF; |
| protected static final int UPPER = 0xFFFF0000; |
| |
| /** |
| * This field stores the resource modification stamp in the lower two bytes, |
| * and the character set generation count in the higher two bytes. |
| */ |
| protected volatile int charsetAndContentId = 0; |
| |
| /** |
| * The file system root that this resource is stored in |
| */ |
| protected FileStoreRoot fileStoreRoot; |
| |
| /** Set of flags which reflect various states of the info (used, derived, ...). */ |
| protected int flags = 0; |
| |
| /** Local sync info */ |
| // thread safety: (Concurrency004) |
| protected volatile long localInfo = I_NULL_SYNC_INFO; |
| |
| /** |
| * This field stores the sync info generation in the lower two bytes, and |
| * the marker generation count in the upper two bytes. |
| */ |
| protected volatile int markerAndSyncStamp; |
| |
| /** The collection of markers for this resource. */ |
| protected MarkerSet markers = null; |
| |
| /** Modification stamp */ |
| protected long modStamp = 0; |
| |
| /** Unique node identifier */ |
| // thread safety: (Concurrency004) |
| protected volatile long nodeId = 0; |
| |
| /** |
| * The properties which are maintained for the lifecycle of the workspace. |
| * <p> |
| * This field is declared as the implementing class rather than the |
| * interface so we ensure that we get it right since we are making certain |
| * assumptions about the object type w.r.t. casting. |
| */ |
| protected ObjectMap<QualifiedName, Object> sessionProperties = null; |
| |
| /** |
| * The table of sync information. |
| * <p> |
| * This field is declared as the implementing class rather than the |
| * interface so we ensure that we get it right since we are making certain |
| * assumptions about the object type w.r.t. casting. |
| */ |
| protected ObjectMap<QualifiedName, Object> syncInfo = null; |
| |
| /** |
| * Returns the integer value stored in the indicated part of this info's flags. |
| */ |
| protected static int getBits(int flags, int mask, int start) { |
| return (flags & mask) >> start; |
| } |
| |
| /** |
| * Returns the type setting for this info. Valid values are |
| * FILE, FOLDER, PROJECT, |
| */ |
| public static int getType(int flags) { |
| return getBits(flags, M_TYPE, M_TYPE_START); |
| } |
| |
| /** |
| * Returns true if all of the bits indicated by the mask are set. |
| */ |
| public static boolean isSet(int flags, int mask) { |
| return (flags & mask) == mask; |
| } |
| |
| /** |
| * Clears all of the bits indicated by the mask. |
| */ |
| public void clear(int mask) { |
| flags &= ~mask; |
| } |
| |
| public void clearModificationStamp() { |
| modStamp = IResource.NULL_STAMP; |
| } |
| |
| public synchronized void clearSessionProperties() { |
| sessionProperties = null; |
| } |
| |
| @Override |
| public Object clone() { |
| try { |
| return super.clone(); |
| } catch (CloneNotSupportedException e) { |
| return null; // never gets here. |
| } |
| } |
| |
| public int getCharsetGenerationCount() { |
| return charsetAndContentId >> 16; |
| } |
| |
| public int getContentId() { |
| return charsetAndContentId & LOWER; |
| } |
| |
| public FileStoreRoot getFileStoreRoot() { |
| return fileStoreRoot; |
| } |
| |
| /** |
| * Returns the set of flags for this info. |
| */ |
| public int getFlags() { |
| return flags; |
| } |
| |
| /** |
| * Gets the local-relative sync information. |
| */ |
| public long getLocalSyncInfo() { |
| return localInfo; |
| } |
| |
| /** |
| * Returns the marker generation count. |
| * The count is incremented whenever markers on the resource change. |
| */ |
| public int getMarkerGenerationCount() { |
| return markerAndSyncStamp >> 16; |
| } |
| |
| /** |
| * Returns a copy of the collection of makers on this resource. |
| * <code>null</code> is returned if there are none. |
| */ |
| public MarkerSet getMarkers() { |
| return getMarkers(true); |
| } |
| |
| /** |
| * Returns the collection of makers on this resource. |
| * <code>null</code> is returned if there are none. |
| */ |
| public MarkerSet getMarkers(boolean makeCopy) { |
| if (markers == null) |
| return null; |
| return makeCopy ? (MarkerSet) markers.clone() : markers; |
| } |
| |
| public long getModificationStamp() { |
| return modStamp; |
| } |
| |
| public long getNodeId() { |
| return nodeId; |
| } |
| |
| /** |
| * Returns the property store associated with this info. The return value may be null. |
| */ |
| public Object getPropertyStore() { |
| return null; |
| } |
| |
| /** |
| * Returns a copy of the map of this resource session properties. |
| * An empty map is returned if there are none. |
| */ |
| @SuppressWarnings({"unchecked"}) |
| public Map<QualifiedName, Object> getSessionProperties() { |
| // thread safety: (Concurrency001) |
| ObjectMap<QualifiedName, Object> temp = sessionProperties; |
| if (temp == null) |
| temp = new ObjectMap<>(5); |
| else |
| temp = (ObjectMap<QualifiedName, Object>) sessionProperties.clone(); |
| return temp; |
| } |
| |
| /** |
| * Returns the value of the identified session property |
| */ |
| public Object getSessionProperty(QualifiedName name) { |
| // thread safety: (Concurrency001) |
| Map<QualifiedName, Object> temp = sessionProperties; |
| if (temp == null) |
| return null; |
| return temp.get(name); |
| } |
| |
| /** |
| * The parameter to this method is the implementing class rather than the |
| * interface so we ensure that we get it right since we are making certain |
| * assumptions about the object type w.r.t. casting. |
| */ |
| @SuppressWarnings({"unchecked"}) |
| public synchronized ObjectMap<QualifiedName, Object> getSyncInfo(boolean makeCopy) { |
| if (syncInfo == null) |
| return null; |
| return makeCopy ? (ObjectMap<QualifiedName, Object>) syncInfo.clone() : syncInfo; |
| } |
| |
| public synchronized byte[] getSyncInfo(QualifiedName id, boolean makeCopy) { |
| // thread safety: (Concurrency001) |
| byte[] b; |
| if (syncInfo == null) |
| return null; |
| b = (byte[]) syncInfo.get(id); |
| return b == null ? null : (makeCopy ? (byte[]) b.clone() : b); |
| } |
| |
| /** |
| * Returns the sync information generation count. |
| * The count is incremented whenever sync info on the resource changes. |
| */ |
| public int getSyncInfoGenerationCount() { |
| return markerAndSyncStamp & LOWER; |
| } |
| |
| /** |
| * Returns the type setting for this info. Valid values are |
| * FILE, FOLDER, PROJECT, |
| */ |
| public int getType() { |
| return getType(flags); |
| } |
| |
| /** |
| * Increments the charset generation count. |
| * The count is incremented whenever the encoding on the resource changes. |
| */ |
| public void incrementCharsetGenerationCount() { |
| //increment high order bits |
| charsetAndContentId = ((charsetAndContentId + LOWER + 1) & UPPER) + (charsetAndContentId & LOWER); |
| } |
| |
| /** |
| * Mark this resource info as having changed content |
| */ |
| public void incrementContentId() { |
| //increment low order bits |
| charsetAndContentId = (charsetAndContentId & UPPER) + ((charsetAndContentId + 1) & LOWER); |
| } |
| |
| /** |
| * Increments the marker generation count. |
| * The count is incremented whenever markers on the resource change. |
| */ |
| public void incrementMarkerGenerationCount() { |
| //increment high order bits |
| markerAndSyncStamp = ((markerAndSyncStamp + LOWER + 1) & UPPER) + (markerAndSyncStamp & LOWER); |
| } |
| |
| /** |
| * Change the modification stamp to indicate that this resource has changed. |
| * The exact value of the stamp doesn't matter, as long as it can be used to |
| * distinguish two arbitrary resource generations. |
| */ |
| public void incrementModificationStamp() { |
| modStamp++; |
| } |
| |
| /** |
| * Increments the sync information generation count. |
| * The count is incremented whenever sync info on the resource changes. |
| */ |
| public void incrementSyncInfoGenerationCount() { |
| //increment low order bits |
| markerAndSyncStamp = (markerAndSyncStamp & UPPER) + ((markerAndSyncStamp + 1) & LOWER); |
| } |
| |
| /** |
| * Returns true if all of the bits indicated by the mask are set. |
| */ |
| public boolean isSet(int mask) { |
| return (flags & mask) == mask; |
| } |
| |
| public void readFrom(int newFlags, DataInput input) throws IOException { |
| // The flags for this info are read by the visitor (flattener). |
| // See Workspace.readElement(). This allows the reader to look ahead |
| // and see what type of info is being loaded. |
| this.flags = newFlags; |
| localInfo = input.readLong(); |
| nodeId = input.readLong(); |
| charsetAndContentId = input.readInt() & LOWER; |
| modStamp = input.readLong(); |
| } |
| |
| /** |
| * Sets all of the bits indicated by the mask. |
| */ |
| public void set(int mask) { |
| flags |= mask; |
| } |
| |
| /** |
| * Sets the value of the indicated bits to be the given value. |
| */ |
| protected void setBits(int mask, int start, int value) { |
| int baseMask = mask >> start; |
| int newValue = (value & baseMask) << start; |
| // thread safety: (guarantee atomic assignment) |
| int temp = flags; |
| temp &= ~mask; |
| temp |= newValue; |
| flags = temp; |
| } |
| |
| public void setFileStoreRoot(FileStoreRoot fileStoreRoot) { |
| this.fileStoreRoot = fileStoreRoot; |
| } |
| |
| /** |
| * Sets the flags for this info. |
| */ |
| protected void setFlags(int value) { |
| flags = value; |
| } |
| |
| /** |
| * Sets the local-relative sync information. |
| */ |
| public void setLocalSyncInfo(long info) { |
| localInfo = info; |
| } |
| |
| /** |
| * Sets the collection of makers for this resource. |
| * <code>null</code> is passed in if there are no markers. |
| */ |
| public void setMarkers(MarkerSet value) { |
| markers = value; |
| } |
| |
| /** |
| * Sets the resource modification stamp. |
| */ |
| public void setModificationStamp(long value) { |
| this.modStamp = value; |
| } |
| |
| /** |
| * |
| */ |
| public void setNodeId(long id) { |
| nodeId = id; |
| // Resource modification stamp starts from current nodeId |
| // so future generations are distinguishable (bug 160728) |
| if (modStamp == 0) |
| modStamp = nodeId; |
| } |
| |
| /** |
| * Sets the property store associated with this info. The value may be null. |
| */ |
| public void setPropertyStore(Object value) { |
| // needs to be implemented on subclasses |
| } |
| |
| /** |
| * Sets the identified session property to the given value. If |
| * the value is null, the property is removed. |
| */ |
| @SuppressWarnings({"unchecked"}) |
| public synchronized void setSessionProperty(QualifiedName name, Object value) { |
| // thread safety: (Concurrency001) |
| if (value == null) { |
| if (sessionProperties == null) |
| return; |
| ObjectMap<QualifiedName, Object> temp = (ObjectMap<QualifiedName, Object>) sessionProperties.clone(); |
| temp.remove(name); |
| if (temp.isEmpty()) |
| sessionProperties = null; |
| else |
| sessionProperties = temp; |
| } else { |
| ObjectMap<QualifiedName, Object> temp = sessionProperties; |
| if (temp == null) |
| temp = new ObjectMap<>(5); |
| else |
| temp = (ObjectMap<QualifiedName, Object>) sessionProperties.clone(); |
| temp.put(name, value); |
| sessionProperties = temp; |
| } |
| } |
| |
| /** |
| * The parameter to this method is the implementing class rather than the |
| * interface so we ensure that we get it right since we are making certain |
| * assumptions about the object type w.r.t. casting. |
| */ |
| protected void setSyncInfo(ObjectMap<QualifiedName, Object> syncInfo) { |
| this.syncInfo = syncInfo; |
| } |
| |
| public synchronized void setSyncInfo(QualifiedName id, byte[] value) { |
| if (value == null) { |
| //delete sync info |
| if (syncInfo == null) |
| return; |
| syncInfo.remove(id); |
| if (syncInfo.isEmpty()) |
| syncInfo = null; |
| } else { |
| //add sync info |
| if (syncInfo == null) |
| syncInfo = new ObjectMap<>(5); |
| syncInfo.put(id, value.clone()); |
| } |
| } |
| |
| /** |
| * Sets the type for this info to the given value. Valid values are |
| * FILE, FOLDER, PROJECT |
| */ |
| public void setType(int value) { |
| setBits(M_TYPE, M_TYPE_START, value); |
| } |
| |
| /* (non-Javadoc |
| * Method declared on IStringPoolParticipant |
| */ |
| @Override |
| public void shareStrings(StringPool set) { |
| ObjectMap<QualifiedName, Object> map = syncInfo; |
| if (map != null) |
| map.shareStrings(set); |
| map = sessionProperties; |
| if (map != null) |
| map.shareStrings(set); |
| MarkerSet markerSet = markers; |
| if (markerSet != null) |
| markerSet.shareStrings(set); |
| } |
| |
| public void writeTo(DataOutput output) throws IOException { |
| // The flags for this info are written by the visitor (flattener). |
| // See SaveManager.writeElement(). This allows the reader to look ahead |
| // and see what type of info is being loaded. |
| output.writeLong(localInfo); |
| output.writeLong(nodeId); |
| output.writeInt(getContentId()); |
| output.writeLong(modStamp); |
| } |
| } |