001 /*
002 * JBoss DNA (http://www.jboss.org/dna)
003 * See the COPYRIGHT.txt file distributed with this work for information
004 * regarding copyright ownership. Some portions may be licensed
005 * to Red Hat, Inc. under one or more contributor license agreements.
006 * See the AUTHORS.txt file in the distribution for a full listing of
007 * individual contributors.
008 *
009 * Unless otherwise indicated, all code in JBoss DNA is licensed
010 * to you under the terms of the GNU Lesser General Public License as
011 * published by the Free Software Foundation; either version 2.1 of
012 * the License, or (at your option) any later version.
013 *
014 * JBoss DNA is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017 * Lesser General Public License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this software; if not, write to the Free
021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023 */
024 package org.jboss.dna.jcr;
025
026 import java.io.Serializable;
027 import java.util.HashMap;
028 import javax.jcr.PropertyType;
029 import net.jcip.annotations.Immutable;
030 import org.jboss.dna.graph.property.Name;
031 import org.jboss.dna.graph.property.NameFactory;
032 import org.jboss.dna.graph.property.ValueFormatException;
033
034 /**
035 * An immutable identifier for a property definition. Although instances can be serialized, the property definitions are often
036 * stored within the graph as {@link #getString() string values} on a property. These string values can later be
037 * {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
038 * use namespace prefixes, so they are long-lasting and durable.
039 * <p>
040 * What distinguishes one property definition from another is not well documented in the JSR-170 specification. The closest this
041 * 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.
042 * The proposed draft of the JSR-283 specification does clarify this more: Section 4.7.15 says :
043 * </p>
044 * <p>
045 * <quote>"A node type may have two or more property definitions with identical name attributes (the value returned by
046 * ItemDefinition.getName) as long as the definitions are otherwise distinguishable by either the required type attribute (the
047 * value returned by PropertyDefinition.getRequiredType) or the multiple attribute (the value returned by
048 * PropertyDefinition.isMultiple)."</quote>
049 * </p>
050 * <p>
051 * This class is {@link Serializable} and designed to be used as a key in a {@link HashMap}.
052 * </p>
053 */
054 @Immutable
055 public final class PropertyDefinitionId implements Serializable {
056
057 /**
058 * Current version is {@value} .
059 */
060 private static final long serialVersionUID = 1L;
061
062 /**
063 * The string-form of the name that can be used to represent a residual property definition.
064 */
065 public static final String ANY_NAME = JcrNodeType.RESIDUAL_ITEM_NAME;
066
067 private final Name nodeTypeName;
068 private final Name propertyDefinitionName;
069 private final int propertyType;
070 private final boolean allowsMultiple;
071 /**
072 * A cached string representation, which is used for {@link #equals(Object)} and {@link #hashCode()} among other things.
073 */
074 private final String stringRepresentation;
075
076 /**
077 * Create a new identifier for a property definition.
078 *
079 * @param nodeTypeName the name of the node type; may not be null
080 * @param propertyDefinitionName the name of the property definition, which may be a {@link #ANY_NAME residual property}; may
081 * not be null
082 * @param propertyType the required property type for the definition; must be a valid {@link PropertyType} value
083 * @param allowsMultiple true if the property definition should allow multiple values, or false if it is a single-value
084 * property definition
085 */
086 public PropertyDefinitionId( Name nodeTypeName,
087 Name propertyDefinitionName,
088 int propertyType,
089 boolean allowsMultiple ) {
090 this.nodeTypeName = nodeTypeName;
091 this.propertyDefinitionName = propertyDefinitionName;
092 this.propertyType = propertyType;
093 this.allowsMultiple = allowsMultiple;
094 this.stringRepresentation = this.nodeTypeName.getString() + '/' + this.propertyDefinitionName.getString() + '/'
095 + PropertyType.nameFromValue(propertyType) + '/' + (allowsMultiple ? '*' : '1');
096 }
097
098 /**
099 * 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 }