KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > properties > PropertyStore


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.internal.properties;
12
13 import java.util.*;
14 import org.eclipse.core.internal.indexing.IndexCursor;
15 import org.eclipse.core.internal.indexing.ObjectID;
16 import org.eclipse.core.internal.resources.CompatibilityMessages;
17 import org.eclipse.core.internal.resources.ResourceException;
18 import org.eclipse.core.resources.IResource;
19 import org.eclipse.core.resources.IResourceStatus;
20 import org.eclipse.core.runtime.*;
21 import org.eclipse.osgi.util.NLS;
22
23 /**
24  *
25  */

26 public class PropertyStore {
27
28     // The indexed store will maintain the properties
29
protected IndexedStoreWrapper store = null;
30
31     // Add directives
32
public static final int CREATE = 0; // must not exist
33
public static final int UPDATE = 1; // must exist
34
public static final int SET_UPDATE = 2; // create if doesn't exist, update if exists
35
public static final int SET_SKIP = 3; // create if doesn't exist, don't update if exists
36

37     // Remove directives
38
public static final int IGNORE_MISSING = 0;
39     public static final int FAIL_MISSING = 1;
40
41     public PropertyStore(IPath location) {
42         store = new IndexedStoreWrapper(location);
43     }
44
45     protected boolean basicExists(StoreKey searchKey) throws CoreException {
46         byte[] searchBytes = searchKey.toBytes();
47         IndexCursor cursor = store.getCursor();
48         try {
49             cursor.find(searchBytes);
50             boolean exists = cursor.keyEquals(searchBytes);
51             cursor.close();
52             return exists;
53         } catch (Exception JavaDoc e) {
54             String JavaDoc message = NLS.bind(CompatibilityMessages.properties_couldNotReadProp, searchKey.getQualifier(), searchKey.getLocalName());
55             throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, searchKey.getResourceName().getPath(), message, e);
56         }
57     }
58
59     /**
60      * The caller is responsible for ensuring that this will not produce
61      * duplicate keys in the index.
62      */

63     protected void basicInsert(StoreKey key, String JavaDoc value) throws CoreException {
64         try {
65             ObjectID valueID = store.createObject(value);
66             store.getIndex().insert(key.toBytes(), valueID);
67         } catch (Exception JavaDoc e) {
68             String JavaDoc message = NLS.bind(CompatibilityMessages.properties_couldNotWriteProp, key.getQualifier(), key.getLocalName());
69             throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, key.getResourceName().getPath(), message, e);
70         }
71     }
72
73     protected boolean basicRemove(ResourceName resourceName, QualifiedName propertyName) throws CoreException {
74         StoreKey key = new StoreKey(resourceName, propertyName);
75         byte[] keyBytes = key.toBytes();
76         boolean wasFound = false;
77         IndexCursor cursor = store.getCursor();
78         try {
79             cursor.find(keyBytes);
80             if (cursor.keyEquals(keyBytes)) {
81                 wasFound = true;
82                 ObjectID valueID = cursor.getValueAsObjectID();
83                 store.removeObject(valueID);
84                 cursor.remove();
85             }
86             cursor.close();
87         } catch (Exception JavaDoc e) {
88             String JavaDoc message = NLS.bind(CompatibilityMessages.properties_couldNotDeleteProp, key.getQualifier(), key.getLocalName());
89             throw new ResourceException(IResourceStatus.FAILED_DELETE_LOCAL, resourceName.getPath(), message, e);
90         }
91         return wasFound;
92     }
93
94     protected void basicUpdate(StoreKey key, String JavaDoc value) throws CoreException {
95         byte[] keyBytes = key.toBytes();
96         IndexCursor cursor = store.getCursor();
97         try {
98             cursor.find(keyBytes);
99             if (cursor.keyEquals(keyBytes)) {
100                 ObjectID oldID = cursor.getValueAsObjectID();
101                 store.removeObject(oldID);
102                 ObjectID newValueId = store.createObject(value);
103                 cursor.updateValue(newValueId);
104             }
105             cursor.close();
106         } catch (Exception JavaDoc e) {
107             String JavaDoc message = NLS.bind(CompatibilityMessages.properties_couldNotWriteProp, key.getQualifier(), key.getLocalName());
108             throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, key.getResourceName().getPath(), message, e);
109         }
110     }
111
112     protected synchronized void commonSet(ResourceName resourceName, StoredProperty[] properties, int depth, int setMode, QueryResults failures) throws CoreException {
113         if (depth == IResource.DEPTH_ZERO) {
114             for (int i = 0; i < properties.length; i++) {
115                 StoredProperty property = properties[i];
116                 StoreKey key = new StoreKey(resourceName, property.getName());
117                 boolean exists = basicExists(key);
118                 if ((exists && (setMode == CREATE)) || (!exists && (setMode == UPDATE)))
119                     failures.add(resourceName, property);
120                 else if (exists && (setMode != SET_SKIP))
121                     basicUpdate(key, property.getStringValue());
122                 else
123                     basicInsert(key, property.getStringValue());
124             }
125         } else {
126             Enumeration resourceNamesEnum = deepResourceNames(resourceName);
127             while (resourceNamesEnum.hasMoreElements())
128                 commonSet((ResourceName) resourceNamesEnum.nextElement(), properties, IResource.DEPTH_ZERO, setMode, failures);
129         }
130     }
131
132     /**
133      * Returns the names of all resources that are rooted at the given resource.
134      * <p>
135      * Answers an <code>Enumeration</code> of <code>IResourceName</code>.
136      * The enumerator will include (at least) <code>resourceName</code> if it
137      * exists. If <code>resourceName</code> does not exist returns an empty
138      * enumerator.
139      *
140      * @see ResourceName
141      * @param resourceName the name of the top most resource to match.
142      * @return an enumeration of matching resource names.
143      */

144     public Enumeration deepResourceNames(ResourceName resourceName) throws CoreException {
145         final Set resultHolder = new HashSet(10);
146         IVisitor visitor = new IVisitor() {
147             public void visit(ResourceName resourceName, StoredProperty property, IndexCursor cursor) {
148                 resultHolder.add(resourceName);
149             }
150
151             public boolean requiresValue(ResourceName resourceName, QualifiedName propertyName) {
152                 return false;
153             }
154         };
155         recordsDeepMatching(resourceName, visitor);
156         return Collections.enumeration(resultHolder);
157     }
158
159     /**
160      * Returns the named property for the given resource.
161      * <p>
162      * The retieval is performed to depth zero. Returns <code>null</code>
163      * if there is no such property defined on the resource.
164      *
165      * @param resourceName the resource name to match.
166      * @param propertyName the property name to match.
167      * @return the matching property, or <code>null</code> if no such property.
168      */

169     public StoredProperty get(ResourceName resourceName, final QualifiedName propertyName) throws CoreException {
170         final Object JavaDoc[] resultHolder = new Object JavaDoc[1];
171         IVisitor simpleVisitor = new IVisitor() {
172             public void visit(ResourceName resourceName, StoredProperty property, IndexCursor cursor) {
173                 resultHolder[0] = property;
174             }
175
176             public boolean requiresValue(ResourceName resourceName, QualifiedName propertyName) {
177                 return true;
178             }
179         };
180         recordsMatching(resourceName, propertyName, simpleVisitor);
181         return (StoredProperty) resultHolder[0];
182     }
183
184     /**
185      * Returns all the properties for a given resource.
186      * <p>
187      * Answer a <code>QueryResults</code> containing <code>StoredProperty</code>.
188      * If there are no matches returns an empty <code>QueryResults</code></p>
189      * <p>
190      * The depth parameter allows searching based on resource name path prefix.</p>
191      *
192      * @see QueryResults
193      * @param resourceName the resource name to match.
194      * @param depth the scope of the query
195      * @return a <code>QueryResults</code> with the matching properties.
196      */

197     public QueryResults getAll(ResourceName resourceName, int depth) throws CoreException {
198         final QueryResults result = new QueryResults();
199         IVisitor visitor = new IVisitor() {
200             public void visit(ResourceName resourceName, StoredProperty property, IndexCursor cursor) {
201                 result.add(resourceName, property);
202             }
203
204             public boolean requiresValue(ResourceName resourceName, QualifiedName propertyName) {
205                 return true;
206             }
207         };
208         if (depth == IResource.DEPTH_ZERO)
209             recordsMatching(resourceName, visitor);
210         else
211             recordsDeepMatching(resourceName, visitor);
212         return result;
213     }
214
215     /**
216      * Returns all the property names for a given resource.
217      * <p>
218      * The result is a <code>QueryResults</code> containing <code>QualifiedName</code>.
219      * If the resource has no defined properties, the method returns
220      * an empty <code>QueryResults</code>.</p>
221      * <p>
222      * The depth parameter allows searching based on resource name path prefix.</p>
223      *
224      * @param resourceName the resource name to match.
225      * @param depth the depth to which the query runs.
226      * @return a <code>QueryResults</code> containing the property names.
227      */

228     public QueryResults getNames(ResourceName resourceName, int depth) throws CoreException {
229         QueryResults results = new QueryResults();
230         if (depth == IResource.DEPTH_ZERO)
231             recordsMatching(resourceName, propertyNameVisitor(results));
232         else
233             recordsDeepMatching(resourceName, propertyNameVisitor(results));
234         return results;
235     }
236
237     /**
238      * Returns true if the property store is up and running. Returns false if
239      * the store has been shutdown.
240      */

241     public boolean isRunning() {
242         return store != null;
243     }
244
245     protected IVisitor propertyNameVisitor(final QueryResults results) {
246         return new IVisitor() {
247             public void visit(ResourceName resourceName, StoredProperty property, IndexCursor cursor) {
248                 results.add(resourceName, property.getName());
249             }
250
251             public boolean requiresValue(ResourceName resourceName, QualifiedName propertyName) {
252                 return false;
253             }
254         };
255     }
256
257     /**
258      * Matches all properties for a given resource.
259      */

260     protected void recordsDeepMatching(ResourceName resourceName, IVisitor visitor) throws CoreException {
261
262         // Build the partial 'search' key
263
StoreKey searchKey = new StoreKey(resourceName, true);
264         byte[] searchBytes = searchKey.toBytes();
265         int probe = searchBytes.length;
266         // Position a cursor over the first matching key
267
IndexCursor cursor = store.getCursor();
268         try {
269             cursor.find(searchBytes);
270
271             // While we have a prefix match
272
while (cursor.keyMatches(searchBytes)) {
273                 // Must check that the prefix is up to a valid path segment
274
// note that the matching bytes length is > search key length since
275
// properties MUST have a local name.
276
byte[] matchingBytes = cursor.getKey();
277                 if (probe == 1 || //empty path is a valid prefix for all paths
278
(matchingBytes[probe] == 0) || // a full path match
279
(matchingBytes[probe] == 47 /*IPath.SEPARATOR*/)) {
280                     // a segment boundary match
281
visitPropertyAt(cursor, visitor);
282                 }
283                 // else the match is intra-segment and therefore invalid
284
cursor.next();
285             }
286             cursor.close();
287         } catch (Exception JavaDoc e) {
288             throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, resourceName.getPath(), CompatibilityMessages.properties_storeProblem, e);
289         }
290     }
291
292     /**
293      * Matches all properties for a given resource.
294      */

295     protected void recordsMatching(ResourceName resourceName, IVisitor visitor) throws CoreException {
296
297         // Build the partial 'search' key
298
StoreKey searchKey = new StoreKey(resourceName, false);
299         byte[] searchBytes = searchKey.toBytes();
300         // Position a cursor over the first matching key
301
IndexCursor cursor = store.getCursor();
302         try {
303             cursor.find(searchBytes);
304
305             // While we have a prefix match, evaluate the visitor
306
while (cursor.keyMatches(searchBytes)) {
307                 visitPropertyAt(cursor, visitor);
308                 cursor.next();
309             }
310             cursor.close();
311         } catch (Exception JavaDoc e) {
312             store.reset();
313             throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, resourceName.getPath(), CompatibilityMessages.properties_storeProblem, e);
314         }
315     }
316
317     /**
318      * Matches the given property for a given resource.
319      * Note that there should be only one.
320      */

321     protected void recordsMatching(ResourceName resourceName, QualifiedName propertyName, IVisitor visitor) throws CoreException {
322
323         // Build the full 'search' key
324
StoreKey searchKey = new StoreKey(resourceName, propertyName);
325         byte[] searchBytes = searchKey.toBytes();
326         // Position a cursor over the first matching key
327
IndexCursor cursor = store.getCursor();
328         try {
329             cursor.find(searchBytes);
330
331             // If we have an exact match, evaluate the visitor
332
if (cursor.keyEquals(searchBytes))
333                 visitPropertyAt(cursor, visitor);
334             cursor.close();
335         } catch (Exception JavaDoc e) {
336             store.reset();
337             throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, resourceName.getPath(), CompatibilityMessages.properties_storeProblem, e);
338         }
339     }
340
341     /**
342      * Remove the given collection of named properties from the given resource.
343      * <p>
344      * All of the properties being removed must exist already on the
345      * resource based on the removeRule parameter. If the rule is
346      * MISSING_IGNORE then attempts to remove properties that do not exist
347      * are ignored, if the rule is MISSING_FAIL the method will throw
348      * a <code>PropertyNotFoundException</code> if the property does not exist.
349      * <p>
350      * If an exception is thrown, all properties that did previously
351      * exist will have been removed from the resource. To determine which
352      * properties caused the exception see the offenders result in the exception.</p>
353      * <p>
354      * The depth parameter allows matching based on resource name path prefix.</p>
355      *
356      * @param resourceName the resource containing the properties.
357      * @param propertyNames the property names to remove.
358      * @param depth the scope for matching the resource name.
359      * @param removeRule the behavior when removing non-existant properties.
360      * @exception CoreException
361      */

362     public QueryResults remove(ResourceName resourceName, QualifiedName[] propertyNames, int depth, int removeRule) throws CoreException {
363         QueryResults failures = new QueryResults();
364         if (depth == IResource.DEPTH_ZERO) {
365             for (int i = 0; i < propertyNames.length; i++) {
366                 boolean found = basicRemove(resourceName, propertyNames[i]);
367                 if (!found && (removeRule == FAIL_MISSING))
368                     failures.add(resourceName, propertyNames[i]);
369             }
370         } else {
371             Enumeration resourceNamesEnum = deepResourceNames(resourceName);
372             while (resourceNamesEnum.hasMoreElements()) {
373                 ResourceName resName = (ResourceName) resourceNamesEnum.nextElement();
374                 for (int i = 0; i < propertyNames.length; i++) {
375                     boolean found = basicRemove(resName, propertyNames[i]);
376                     if (!found && (removeRule == FAIL_MISSING))
377                         failures.add(resName, propertyNames[i]);
378                 }
379             }
380         }
381         return failures;
382     }
383
384     /**
385      * Remove the named property from the given resource.
386      * <p>
387      * If a matching property does not exist on this resource
388      * the method has no affect on the store. Removal is performed
389      * to depth zero.</p>
390      * <p>
391      * @param resourceName the resource containing the property.
392      * @param propertyName the property to remove.
393      */

394     public void remove(ResourceName resourceName, QualifiedName propertyName) throws CoreException {
395         remove(resourceName, new QualifiedName[] {propertyName}, IResource.DEPTH_ZERO, IGNORE_MISSING);
396     }
397
398     /**
399      * Remove all the properties from a given resource.
400      * <p>
401      * The depth parameter allows matching based on resource name path prefix.</p>
402      *
403      * @param resourceName the resource containing the properties.
404      * @param depth the scope for matching the resource name.
405      */

406     public void removeAll(ResourceName resourceName, int depth) throws CoreException {
407         QueryResults namesSearch = getNames(resourceName, depth);
408         Enumeration resourceNamesEnum = namesSearch.getResourceNames();
409         while (resourceNamesEnum.hasMoreElements()) {
410             ResourceName resName = (ResourceName) resourceNamesEnum.nextElement();
411             Enumeration propertyNamesEnum = Collections.enumeration(namesSearch.getResults(resName));
412             while (propertyNamesEnum.hasMoreElements()) {
413                 QualifiedName propertyName = (QualifiedName) propertyNamesEnum.nextElement();
414                 basicRemove(resName, propertyName);
415             }
416         }
417     }
418
419     /**
420      * Sets the given collection of properties on the given resource.
421      * <p>
422      * The addRule determines whether the properties must already exist
423      * or not, and if they do whether they are updated by subsequent addition.
424      * Valid addRule values are defined in <code>IPropertyCollectionConstants</code>.
425      * <p>
426      * The depth parameter allows matching based on resource name path prefix.</p>
427      * <p>
428      * The <code>PropertyExistsException</code> is thrown if the matching resource
429      * already has a property of the same name, and the rule requires that
430      * it must not. If the exception is thrown, all successfull properties
431      * will have been set, and the failures are listed in the exception.</p>
432      *
433      * @param resourceName the resource to receive the properties.
434      * @param properties the properties to add.
435      * @param depth the depth at which to apply the add opertion.
436      * @param mode the behavior of the add operation.
437      * @exception CoreException
438      */

439     public QueryResults set(ResourceName resourceName, StoredProperty[] properties, int depth, int mode) throws CoreException {
440         QueryResults failures = new QueryResults();
441         commonSet(resourceName, properties, depth, mode, failures);
442         return failures;
443     }
444
445     /**
446      * Sets the given property to the given resource.
447      * <p>
448      * The property is added to depth zero. If the resource already has
449      * a property with the same name, its value is updated to the given
450      * value (i.e. SET_UPDATE add rule equivalent.)</p>
451      *
452      * @param resourceName the resource to receive the property.
453      * @param property the property to add.
454      */

455     public void set(ResourceName resourceName, StoredProperty property) throws CoreException {
456         commonSet(resourceName, new StoredProperty[] {property}, IResource.DEPTH_ZERO, SET_UPDATE, null);
457     }
458
459     public void shutdown(IProgressMonitor monitor) {
460         if (store == null)
461             return;
462         try {
463             store.close();
464         } finally {
465             //null the store so other threads with a handle on it cannot use it
466
store = null;
467         }
468     }
469
470     public void startup(IProgressMonitor monitor) {
471         //do nothing
472
}
473
474     protected void visitPropertyAt(IndexCursor cursor, IVisitor visitor) throws CoreException {
475         try {
476             StoreKey key = new StoreKey(cursor.getKey());
477             ResourceName resourceName = key.getResourceName();
478             QualifiedName propertyName = key.getPropertyName();
479             String JavaDoc propertyValue = null;
480             if (visitor.requiresValue(resourceName, propertyName))
481                 propertyValue = store.getObjectAsString(cursor.getValueAsObjectID());
482             visitor.visit(resourceName, new StoredProperty(propertyName, propertyValue), cursor);
483         } catch (Exception JavaDoc e) {
484             throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, null, CompatibilityMessages.properties_storeProblem, e);
485         }
486     }
487
488     public void commit() throws CoreException {
489         store.commit();
490     }
491 }
492
Popular Tags