View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6   * See the AUTHORS.txt file in the distribution for a full listing of 
7   * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.repository.sequencer;
25  
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.Iterator;
30  import java.util.LinkedList;
31  import java.util.List;
32  import java.util.Map;
33  import net.jcip.annotations.Immutable;
34  import net.jcip.annotations.NotThreadSafe;
35  import org.modeshape.common.util.CheckArg;
36  import org.modeshape.graph.JcrLexicon;
37  import org.modeshape.graph.property.Name;
38  import org.modeshape.graph.property.Path;
39  import org.modeshape.graph.property.PathFactory;
40  import org.modeshape.graph.property.ValueFactories;
41  import org.modeshape.graph.sequencer.SequencerOutput;
42  
43  /**
44   * A basic {@link SequencerOutput} that records all information in-memory and which organizes the properties by {@link Path node
45   * paths} and provides access to the nodes in a natural path-order.
46   */
47  @NotThreadSafe
48  public class SequencerOutputMap implements SequencerOutput, Iterable<SequencerOutputMap.Entry> {
49  
50      private final Map<Path, List<PropertyValue>> data;
51      private transient boolean valuesSorted = true;
52      private final ValueFactories factories;
53  
54      public SequencerOutputMap( ValueFactories factories ) {
55          CheckArg.isNotNull(factories, "factories");
56          this.data = new HashMap<Path, List<PropertyValue>>();
57          this.factories = factories;
58      }
59  
60      ValueFactories getFactories() {
61          return this.factories;
62      }
63  
64      /**
65       * {@inheritDoc}
66       */
67      public void setProperty( Path nodePath,
68                               Name propertyName,
69                               Object... values ) {
70          CheckArg.isNotNull(nodePath, "nodePath");
71          CheckArg.isNotNull(propertyName, "property");
72  
73          // Find or create the entry for this node ...
74          List<PropertyValue> properties = this.data.get(nodePath);
75          if (properties == null) {
76              if (values == null || values.length == 0 || (values.length == 1 && values[0] == null)) return; // do nothing
77              properties = new ArrayList<PropertyValue>();
78              this.data.put(nodePath, properties);
79          }
80          if (values == null || values.length == 0 || (values.length == 1 && values[0] == null)) {
81              properties.remove(new PropertyValue(propertyName, null));
82          } else {
83              Object propValue = values.length == 1 ? values[0] : values;
84              PropertyValue value = new PropertyValue(propertyName, propValue);
85              properties.add(value);
86              valuesSorted = false;
87          }
88      }
89  
90      /**
91       * {@inheritDoc}
92       * 
93       * @deprecated As of ModeShape 2.0, the preferred approach is to use {@link #setProperty(Path, Name, Object...)}, which
94       *             properly addresses the session having different namespace mappings. This method depends on the namespace
95       *             mappings for the given URIs in the name components of the nodePath and propertyName to be mapped in the
96       *             NamespaceRegistry of the ModeShapeEngine's (or JcrEngine's) ExecutionContext.
97       */
98      @Deprecated
99      public void setProperty( String nodePath,
100                              String property,
101                              Object... values ) {
102         CheckArg.isNotEmpty(nodePath, "nodePath");
103         CheckArg.isNotEmpty(property, "property");
104         Path path = this.factories.getPathFactory().create(nodePath);
105         Name propertyName = this.factories.getNameFactory().create(property);
106         setProperty(path, propertyName, values);
107     }
108 
109     /**
110      * {@inheritDoc}
111      * 
112      * @deprecated As of ModeShape 2.0, the preferred approach is to use {@link #setProperty(Path, Name, Object...)}, which
113      *             properly addresses the session having different namespace mappings. This method depends on the namespace
114      *             mappings for the given URIs in the name components of the nodePath and propertyName to be mapped in the
115      *             NamespaceRegistry of the ModeShapeEngine's (or JcrEngine's) ExecutionContext.
116      */
117     @Deprecated
118     public void setReference( String nodePath,
119                               String propertyName,
120                               String... paths ) {
121         PathFactory pathFactory = this.factories.getPathFactory();
122         Path path = pathFactory.create(nodePath);
123         Name name = this.factories.getNameFactory().create(propertyName);
124         Object[] values = null;
125         if (paths != null && paths.length != 0) {
126             values = new Path[paths.length];
127             for (int i = 0, len = paths.length; i != len; ++i) {
128                 String pathValue = paths[i];
129                 values[i] = pathFactory.create(pathValue);
130             }
131         }
132         setProperty(path, name, values);
133     }
134 
135     /**
136      * Return the number of node entries in this map.
137      * 
138      * @return the number of entries
139      */
140     public int size() {
141         return this.data.size();
142     }
143 
144     /**
145      * Return whether there are no entries
146      * 
147      * @return true if this container is empty, or false otherwise
148      */
149     public boolean isEmpty() {
150         return this.data.isEmpty();
151     }
152 
153     protected List<PropertyValue> removeProperties( Path nodePath ) {
154         return this.data.remove(nodePath);
155     }
156 
157     /**
158      * Get the properties for the node given by the supplied path.
159      * 
160      * @param nodePath the path to the node
161      * @return the property values, or null if there are none
162      */
163     protected List<PropertyValue> getProperties( Path nodePath ) {
164         return data.get(nodePath);
165     }
166 
167     /**
168      * Return the entries in this output in an order with shorter paths first.
169      * <p>
170      * {@inheritDoc}
171      */
172     public Iterator<Entry> iterator() {
173         LinkedList<Path> paths = new LinkedList<Path>(data.keySet());
174         Collections.sort(paths);
175         sortValues();
176         return new EntryIterator(paths.iterator());
177     }
178 
179     protected void sortValues() {
180         if (!valuesSorted) {
181             for (List<PropertyValue> values : this.data.values()) {
182                 if (values.size() > 1) Collections.sort(values);
183             }
184             valuesSorted = true;
185         }
186     }
187 
188     /**
189      * {@inheritDoc}
190      */
191     @Override
192     public String toString() {
193         return this.data.toString();
194     }
195 
196     /**
197      * A property name and value pair. PropertyValue instances have a natural order where the <code>jcr:primaryType</code> is
198      * first, followed by all other properties in ascending lexicographical order according to the {@link #getName() name}.
199      * 
200      * @author Randall Hauch
201      */
202     @Immutable
203     public class PropertyValue implements Comparable<PropertyValue> {
204 
205         private final Name name;
206         private final Object value;
207 
208         protected PropertyValue( Name propertyName,
209                                  Object value ) {
210             this.name = propertyName;
211             this.value = value;
212         }
213 
214         /**
215          * Get the property name.
216          * 
217          * @return the property name; never null
218          */
219         public Name getName() {
220             return this.name;
221         }
222 
223         /**
224          * Get the property value, which is either a single value or an array of values.
225          * 
226          * @return the property value
227          */
228         public Object getValue() {
229             return this.value;
230         }
231 
232         /**
233          * {@inheritDoc}
234          */
235         public int compareTo( PropertyValue that ) {
236             if (this == that) return 0;
237             if (this.name.equals(JcrLexicon.PRIMARY_TYPE)) return -1;
238             if (that.name.equals(JcrLexicon.PRIMARY_TYPE)) return 1;
239             return this.name.compareTo(that.name);
240         }
241 
242         /**
243          * {@inheritDoc}
244          */
245         @Override
246         public int hashCode() {
247             return this.name.hashCode();
248         }
249 
250         /**
251          * {@inheritDoc}
252          */
253         @Override
254         public boolean equals( Object obj ) {
255             if (obj == this) return true;
256             if (obj instanceof PropertyValue) {
257                 PropertyValue that = (PropertyValue)obj;
258                 if (!this.getName().equals(that.getName())) return false;
259                 return true;
260             }
261             return false;
262         }
263 
264         /**
265          * {@inheritDoc}
266          */
267         @Override
268         public String toString() {
269             return "[" + this.name + "=" + value + "]";
270         }
271     }
272 
273     /**
274      * An entry in a SequencerOutputMap, which contains the path of the node and the {@link #getPropertyValues() property values}
275      * on the node.
276      * 
277      * @author Randall Hauch
278      */
279     @Immutable
280     public class Entry {
281 
282         private final Path path;
283         private final Name primaryType;
284         private final List<PropertyValue> properties;
285 
286         protected Entry( Path path,
287                          List<PropertyValue> properties ) {
288             assert path != null;
289             assert properties != null;
290             this.path = path;
291             this.properties = properties;
292             if (this.properties.size() > 0 && this.properties.get(0).getName().equals("jcr:primaryType")) {
293                 PropertyValue primaryTypeProperty = this.properties.remove(0);
294                 this.primaryType = getFactories().getNameFactory().create(primaryTypeProperty.getValue());
295             } else {
296                 this.primaryType = null;
297             }
298         }
299 
300         /**
301          * @return path
302          */
303         public Path getPath() {
304             return this.path;
305         }
306 
307         /**
308          * Get the primary type specified for this node, or null if the type was not specified
309          * 
310          * @return the primary type, or null
311          */
312         public Name getPrimaryTypeValue() {
313             return this.primaryType;
314         }
315 
316         /**
317          * Get the property values, which may be empty
318          * 
319          * @return value
320          */
321         public List<PropertyValue> getPropertyValues() {
322             return getProperties(path);
323         }
324     }
325 
326     protected class EntryIterator implements Iterator<Entry> {
327 
328         private Path last;
329         private final Iterator<Path> iter;
330 
331         protected EntryIterator( Iterator<Path> iter ) {
332             this.iter = iter;
333         }
334 
335         /**
336          * {@inheritDoc}
337          */
338         public boolean hasNext() {
339             return iter.hasNext();
340         }
341 
342         /**
343          * {@inheritDoc}
344          */
345         public Entry next() {
346             this.last = iter.next();
347             return new Entry(last, getProperties(last));
348         }
349 
350         /**
351          * {@inheritDoc}
352          */
353         public void remove() {
354             if (last == null) throw new IllegalStateException();
355             try {
356                 removeProperties(last);
357             } finally {
358                 last = null;
359             }
360         }
361     }
362 
363 }