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     * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010     * is licensed 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.graph.property.basic;
025    
026    import java.io.InputStream;
027    import java.io.Reader;
028    import java.math.BigDecimal;
029    import java.net.URI;
030    import java.util.Calendar;
031    import java.util.Date;
032    import java.util.UUID;
033    import java.util.regex.Matcher;
034    import java.util.regex.Pattern;
035    import net.jcip.annotations.Immutable;
036    import org.jboss.dna.common.text.TextDecoder;
037    import org.jboss.dna.common.util.CheckArg;
038    import org.jboss.dna.graph.GraphI18n;
039    import org.jboss.dna.graph.property.Binary;
040    import org.jboss.dna.graph.property.DateTime;
041    import org.jboss.dna.graph.property.IoException;
042    import org.jboss.dna.graph.property.Name;
043    import org.jboss.dna.graph.property.NameFactory;
044    import org.jboss.dna.graph.property.NamespaceException;
045    import org.jboss.dna.graph.property.NamespaceRegistry;
046    import org.jboss.dna.graph.property.Path;
047    import org.jboss.dna.graph.property.PropertyType;
048    import org.jboss.dna.graph.property.Reference;
049    import org.jboss.dna.graph.property.ValueFactory;
050    import org.jboss.dna.graph.property.ValueFormatException;
051    
052    /**
053     * The standard {@link ValueFactory} for {@link PropertyType#NAME} values.
054     * 
055     * @author Randall Hauch
056     * @author John Verhaeg
057     */
058    @Immutable
059    public class NameValueFactory extends AbstractValueFactory<Name> implements NameFactory {
060    
061        // Non-escaped pattern: (\{([^}]*)\})?(.*)
062        protected static final String FULLY_QUALFIED_NAME_PATTERN_STRING = "\\{([^}]*)\\}(.*)";
063        protected static final Pattern FULLY_QUALIFIED_NAME_PATTERN = Pattern.compile(FULLY_QUALFIED_NAME_PATTERN_STRING);
064    
065        // Original pattern: (([^:/]*):)?([^:]*)
066        private static final String PREFIXED_NAME_PATTERN_STRING = "(([^:/]*):)?([^:]*)";
067        private static final Pattern PREFIXED_NAME_PATTERN = Pattern.compile(PREFIXED_NAME_PATTERN_STRING);
068    
069        private static Name BLANK_NAME;
070        private static Name ANY_NAME;
071    
072        private final NamespaceRegistry namespaceRegistry;
073    
074        public NameValueFactory( NamespaceRegistry namespaceRegistry,
075                                 TextDecoder decoder,
076                                 ValueFactory<String> stringValueFactory ) {
077            super(PropertyType.NAME, decoder, stringValueFactory);
078            CheckArg.isNotNull(namespaceRegistry, "namespaceRegistry");
079            this.namespaceRegistry = namespaceRegistry;
080        }
081    
082        /**
083         * {@inheritDoc}
084         */
085        public Name create( String value ) {
086            return create(value, getDecoder());
087        }
088    
089        /**
090         * {@inheritDoc}
091         */
092        public Name create( String value,
093                            TextDecoder decoder ) {
094            if (value == null) return null;
095            if (decoder == null) decoder = getDecoder();
096            try {
097                if (value.length() == 0) {
098                    if (BLANK_NAME == null) BLANK_NAME = new BasicName("", "");
099                    return BLANK_NAME;
100                }
101                char firstChar = value.charAt(0);
102                if (value.length() == 1 && firstChar == '*') {
103                    if (ANY_NAME == null) ANY_NAME = new BasicName("", "*");
104                    return ANY_NAME;
105                }
106                if (firstChar != '{') {
107                    // First, see whether the value fits the prefixed name pattern ...
108                    Matcher matcher = PREFIXED_NAME_PATTERN.matcher(value);
109                    if (matcher.matches()) {
110                        String prefix = matcher.group(2);
111                        String localName = matcher.group(3);
112                        // Decode the parts ...
113                        prefix = prefix == null ? "" : decoder.decode(prefix);
114                        localName = decoder.decode(localName);
115                        // Look for a namespace match ...
116                        String namespaceUri = this.namespaceRegistry.getNamespaceForPrefix(prefix);
117                        // Fail if no namespace is found ...
118                        if (namespaceUri == null) {
119                            throw new NamespaceException(GraphI18n.noNamespaceRegisteredForPrefix.text(prefix));
120                        }
121                        return new BasicName(namespaceUri, localName);
122                    }
123                }
124                // If it doesn't fit the prefixed pattern, then try the internal pattern
125                Matcher matcher = FULLY_QUALIFIED_NAME_PATTERN.matcher(value);
126                if (matcher.matches()) {
127                    String namespaceUri = matcher.group(1);
128                    String localName = matcher.group(2);
129                    // Decode the parts ...
130                    namespaceUri = decoder.decode(namespaceUri);
131                    localName = decoder.decode(localName);
132                    return new BasicName(namespaceUri, localName);
133                }
134            } catch (NamespaceException err) {
135                throw new ValueFormatException(value, getPropertyType(),
136                                               GraphI18n.errorConvertingType.text(String.class.getSimpleName(),
137                                                                                  Name.class.getSimpleName(),
138                                                                                  value), err);
139            }
140            throw new ValueFormatException(value, getPropertyType(), GraphI18n.errorConvertingType.text(String.class.getSimpleName(),
141                                                                                                        Name.class.getSimpleName(),
142                                                                                                        value));
143        }
144    
145        /**
146         * {@inheritDoc}
147         */
148        public Name create( String namespaceUri,
149                            String localName ) {
150            return create(namespaceUri, localName, getDecoder());
151        }
152    
153        /**
154         * {@inheritDoc}
155         */
156        public Name create( String namespaceUri,
157                            String localName,
158                            TextDecoder decoder ) {
159            CheckArg.isNotEmpty(localName, "localName");
160            if (decoder == null) decoder = getDecoder();
161            namespaceUri = namespaceUri != null ? decoder.decode(namespaceUri.trim()) : null;
162            localName = decoder.decode(localName.trim());
163            return new BasicName(namespaceUri, localName);
164        }
165    
166        /**
167         * {@inheritDoc}
168         */
169        public Name create( int value ) {
170            throw new ValueFormatException(value, getPropertyType(),
171                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
172                                                                              Integer.class.getSimpleName(),
173                                                                              value));
174        }
175    
176        /**
177         * {@inheritDoc}
178         */
179        public Name create( long value ) {
180            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
181                                                                                                        Long.class.getSimpleName(),
182                                                                                                        value));
183        }
184    
185        /**
186         * {@inheritDoc}
187         */
188        public Name create( boolean value ) {
189            throw new ValueFormatException(value, getPropertyType(),
190                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
191                                                                              Boolean.class.getSimpleName(),
192                                                                              value));
193        }
194    
195        /**
196         * {@inheritDoc}
197         */
198        public Name create( float value ) {
199            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
200                                                                                                        Float.class.getSimpleName(),
201                                                                                                        value));
202        }
203    
204        /**
205         * {@inheritDoc}
206         */
207        public Name create( double value ) {
208            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
209                                                                                                        Double.class.getSimpleName(),
210                                                                                                        value));
211        }
212    
213        /**
214         * {@inheritDoc}
215         */
216        public Name create( BigDecimal value ) {
217            throw new ValueFormatException(value, getPropertyType(),
218                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
219                                                                              BigDecimal.class.getSimpleName(),
220                                                                              value));
221        }
222    
223        /**
224         * {@inheritDoc}
225         */
226        public Name create( Calendar value ) {
227            throw new ValueFormatException(value, getPropertyType(),
228                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
229                                                                              Calendar.class.getSimpleName(),
230                                                                              value));
231        }
232    
233        /**
234         * {@inheritDoc}
235         */
236        public Name create( Date value ) {
237            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
238                                                                                                        Date.class.getSimpleName(),
239                                                                                                        value));
240        }
241    
242        /**
243         * {@inheritDoc}
244         * 
245         * @see org.jboss.dna.graph.property.ValueFactory#create(org.jboss.dna.graph.property.DateTime)
246         */
247        public Name create( DateTime value ) throws ValueFormatException {
248            throw new ValueFormatException(value, getPropertyType(),
249                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
250                                                                              DateTime.class.getSimpleName(),
251                                                                              value));
252        }
253    
254        /**
255         * {@inheritDoc}
256         */
257        public Name create( Name value ) {
258            return value;
259        }
260    
261        /**
262         * {@inheritDoc}
263         */
264        public Name create( Path value ) {
265            if (value == null) return null;
266            if (!value.isAbsolute() && value.size() == 1) {
267                // A relative name of length 1 is converted to a name
268                Path.Segment segment = value.getLastSegment();
269                // Can only convert if the path has no SNS index ...
270                if (!segment.hasIndex()) return segment.getName();
271            }
272            throw new ValueFormatException(value, getPropertyType(), GraphI18n.errorConvertingType.text(Path.class.getSimpleName(),
273                                                                                                        Name.class.getSimpleName(),
274                                                                                                        value));
275        }
276    
277        /**
278         * {@inheritDoc}
279         */
280        public Name create( Reference value ) {
281            throw new ValueFormatException(value, getPropertyType(),
282                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
283                                                                              Reference.class.getSimpleName(),
284                                                                              value));
285        }
286    
287        /**
288         * {@inheritDoc}
289         */
290        public Name create( URI value ) {
291            if (value == null) return null;
292            String asciiString = value.toASCIIString();
293            // Remove any leading "./" ...
294            if (asciiString.startsWith("./") && asciiString.length() > 2) {
295                asciiString = asciiString.substring(2);
296            }
297            if (asciiString.indexOf('/') == -1) {
298                return create(asciiString);
299            }
300            throw new ValueFormatException(value, getPropertyType(), GraphI18n.errorConvertingType.text(URI.class.getSimpleName(),
301                                                                                                        Path.class.getSimpleName(),
302                                                                                                        value));
303        }
304    
305        /**
306         * {@inheritDoc}
307         * 
308         * @see org.jboss.dna.graph.property.ValueFactory#create(java.util.UUID)
309         */
310        public Name create( UUID value ) throws IoException {
311            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
312                                                                                                        UUID.class.getSimpleName(),
313                                                                                                        value));
314        }
315    
316        /**
317         * {@inheritDoc}
318         */
319        public Name create( byte[] value ) {
320            // First attempt to create a string from the value, then a long from the string ...
321            return create(getStringValueFactory().create(value));
322        }
323    
324        /**
325         * {@inheritDoc}
326         * 
327         * @see org.jboss.dna.graph.property.ValueFactory#create(org.jboss.dna.graph.property.Binary)
328         */
329        public Name create( Binary value ) throws ValueFormatException, IoException {
330            // First create a string and then create the boolean from the string value ...
331            return create(getStringValueFactory().create(value));
332        }
333    
334        /**
335         * {@inheritDoc}
336         */
337        public Name create( InputStream stream,
338                            long approximateLength ) throws IoException {
339            // First attempt to create a string from the value, then a double from the string ...
340            return create(getStringValueFactory().create(stream, approximateLength));
341        }
342    
343        /**
344         * {@inheritDoc}
345         */
346        public Name create( Reader reader,
347                            long approximateLength ) throws IoException {
348            // First attempt to create a string from the value, then a double from the string ...
349            return create(getStringValueFactory().create(reader, approximateLength));
350        }
351    
352        /**
353         * <p>
354         * {@inheritDoc}
355         * </p>
356         * 
357         * @see org.jboss.dna.graph.property.NameFactory#getNamespaceRegistry()
358         */
359        public NamespaceRegistry getNamespaceRegistry() {
360            return namespaceRegistry;
361        }
362    
363        /**
364         * {@inheritDoc}
365         */
366        @Override
367        protected Name[] createEmptyArray( int length ) {
368            return new Name[length];
369        }
370    }