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 }