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    * Unless otherwise indicated, all code in ModeShape is licensed
10   * 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.jcr;
25  
26  import java.io.Serializable;
27  import java.util.HashMap;
28  import javax.jcr.PropertyType;
29  import net.jcip.annotations.Immutable;
30  import org.modeshape.graph.property.Name;
31  import org.modeshape.graph.property.NameFactory;
32  import org.modeshape.graph.property.ValueFormatException;
33  
34  /**
35   * An immutable identifier for a property definition. Although instances can be serialized, the property definitions are often
36   * stored within the graph as {@link #getString() string values} on a property. These string values can later be
37   * {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
38   * use namespace prefixes, so they are long-lasting and durable.
39   * <p>
40   * What distinguishes one property definition from another is not well documented in the JSR-170 specification. The closest this
41   * version of the spec gets is Section 6.7.15, but that merely says that more than one property definition can have the same name.
42   * The proposed draft of the JSR-283 specification does clarify this more: Section 4.7.15 says :
43   * </p>
44   * <p>
45   * <quote>"A node type may have two or more property definitions with identical name attributes (the value returned by
46   * ItemDefinition.getName) as long as the definitions are otherwise distinguishable by either the required type attribute (the
47   * value returned by PropertyDefinition.getRequiredType) or the multiple attribute (the value returned by
48   * PropertyDefinition.isMultiple)."</quote>
49   * </p>
50   * <p>
51   * This class is {@link Serializable} and designed to be used as a key in a {@link HashMap}.
52   * </p>
53   */
54  @Immutable
55  final class PropertyDefinitionId implements Serializable {
56  
57      /**
58       * Current version is {@value} .
59       */
60      private static final long serialVersionUID = 1L;
61  
62      /**
63       * The string-form of the name that can be used to represent a residual property definition.
64       */
65      public static final String ANY_NAME = JcrNodeType.RESIDUAL_ITEM_NAME;
66  
67      private final Name nodeTypeName;
68      private final Name propertyDefinitionName;
69      private final int propertyType;
70      private final boolean allowsMultiple;
71      /**
72       * A cached string representation, which is used for {@link #equals(Object)} and {@link #hashCode()} among other things.
73       */
74      private final String stringRepresentation;
75  
76      /**
77       * Create a new identifier for a property definition.
78       * 
79       * @param nodeTypeName the name of the node type; may not be null
80       * @param propertyDefinitionName the name of the property definition, which may be a {@link #ANY_NAME residual property}; may
81       *        not be null
82       * @param propertyType the required property type for the definition; must be a valid {@link PropertyType} value
83       * @param allowsMultiple true if the property definition should allow multiple values, or false if it is a single-value
84       *        property definition
85       */
86      public PropertyDefinitionId( Name nodeTypeName,
87                                   Name propertyDefinitionName,
88                                   int propertyType,
89                                   boolean allowsMultiple ) {
90          this.nodeTypeName = nodeTypeName;
91          this.propertyDefinitionName = propertyDefinitionName;
92          this.propertyType = propertyType;
93          this.allowsMultiple = allowsMultiple;
94          this.stringRepresentation = this.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString() + '/'
95                                      + PropertyType.nameFromValue(propertyType) + '/' + (allowsMultiple ? '*' : '1');
96      }
97  
98      /**
99       * Get the name of the node type on which the property definition is defined
100      * 
101      * @return the node type's name; may not be null
102      */
103     public Name getNodeTypeName() {
104         return nodeTypeName;
105     }
106 
107     /**
108      * Get the name of the property definition.
109      * 
110      * @return the property definition's name; never null
111      */
112     public Name getPropertyDefinitionName() {
113         return propertyDefinitionName;
114     }
115 
116     /**
117      * Get the required property type
118      * 
119      * @return the property type; always a valid {@link PropertyType} value
120      */
121     public int getPropertyType() {
122         return propertyType;
123     }
124 
125     /**
126      * Return whether the property definition allows multiple values.
127      * 
128      * @return true if the property definition allows multiple values, or false if it is a single-value property definition
129      */
130     public boolean allowsMultiple() {
131         return allowsMultiple;
132     }
133 
134     /**
135      * Determine whether this property definition allows properties with any name.
136      * 
137      * @return true if this node definition allows properties with any name, or false if this definition requires a particular
138      *         property name
139      */
140     public boolean allowsAnyChildName() {
141         return propertyDefinitionName.getLocalName().equals(ANY_NAME) && propertyDefinitionName.getNamespaceUri().length() == 0;
142     }
143 
144     /**
145      * Get the string form of this identifier. This form can be persisted, since it does not rely upon namespace prefixes.
146      * 
147      * @return the string form
148      */
149     public String getString() {
150         return this.stringRepresentation;
151     }
152 
153     /**
154      * Parse the supplied string for of an identifer, and return the object form for that identifier.
155      * 
156      * @param definition the {@link #getString() string form of the identifier}; may not be null
157      * @param factory the factory that should be used to create Name objects; may not be null
158      * @return the object form of the identifier; never null
159      * @throws ValueFormatException if the definition is not the valid format
160      */
161     public static PropertyDefinitionId fromString( String definition,
162                                                    NameFactory factory ) {
163         String[] parts = definition.split("/");
164         String nodeTypeNameString = parts[0];
165         String propertyDefinitionNameString = parts[1];
166         Name nodeTypeName = factory.create(nodeTypeNameString);
167         Name propertyDefinitionName = factory.create(propertyDefinitionNameString);
168         int propertyType = PropertyType.valueFromName(parts[2]);
169         boolean allowsMultiple = parts[3].charAt(0) == '*';
170         return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName, propertyType, allowsMultiple);
171     }
172 
173     public PropertyDefinitionId asSingleValued() {
174         return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName, propertyType, false);
175     }
176 
177     public PropertyDefinitionId asMultiValued() {
178         return new PropertyDefinitionId(nodeTypeName, propertyDefinitionName, propertyType, true);
179     }
180 
181     /**
182      * {@inheritDoc}
183      * 
184      * @see java.lang.Object#hashCode()
185      */
186     @Override
187     public int hashCode() {
188         return this.stringRepresentation.hashCode();
189     }
190 
191     /**
192      * {@inheritDoc}
193      * 
194      * @see java.lang.Object#equals(java.lang.Object)
195      */
196     @Override
197     public boolean equals( Object obj ) {
198         if (obj == this) return true;
199         if (obj instanceof PropertyDefinitionId) {
200             PropertyDefinitionId that = (PropertyDefinitionId)obj;
201             return this.stringRepresentation.equals(that.stringRepresentation);
202         }
203         return false;
204     }
205 
206     /**
207      * {@inheritDoc}
208      * 
209      * @see java.lang.Object#toString()
210      */
211     @Override
212     public String toString() {
213         return this.stringRepresentation;
214     }
215 
216 }