001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors.
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    package org.jboss.dna.graph.properties.basic;
023    
024    import java.io.InputStream;
025    import java.io.Reader;
026    import java.math.BigDecimal;
027    import java.net.URI;
028    import java.util.Calendar;
029    import java.util.Date;
030    import java.util.UUID;
031    import java.util.regex.Matcher;
032    import java.util.regex.Pattern;
033    import net.jcip.annotations.Immutable;
034    import org.jboss.dna.common.text.TextDecoder;
035    import org.jboss.dna.common.util.CheckArg;
036    import org.jboss.dna.graph.GraphI18n;
037    import org.jboss.dna.graph.properties.Binary;
038    import org.jboss.dna.graph.properties.DateTime;
039    import org.jboss.dna.graph.properties.IoException;
040    import org.jboss.dna.graph.properties.Name;
041    import org.jboss.dna.graph.properties.NameFactory;
042    import org.jboss.dna.graph.properties.NamespaceException;
043    import org.jboss.dna.graph.properties.NamespaceRegistry;
044    import org.jboss.dna.graph.properties.Path;
045    import org.jboss.dna.graph.properties.PropertyType;
046    import org.jboss.dna.graph.properties.Reference;
047    import org.jboss.dna.graph.properties.ValueFactory;
048    import org.jboss.dna.graph.properties.ValueFormatException;
049    
050    /**
051     * The standard {@link ValueFactory} for {@link PropertyType#NAME} values.
052     * 
053     * @author Randall Hauch
054     * @author John Verhaeg
055     */
056    @Immutable
057    public class NameValueFactory extends AbstractValueFactory<Name> implements NameFactory {
058    
059        // Non-escaped pattern: (\{([^}]*)\})?(.*)
060        protected static final String FULLY_QUALFIED_NAME_PATTERN_STRING = "\\{([^}]*)\\}(.*)";
061        protected static final Pattern FULLY_QUALIFIED_NAME_PATTERN = Pattern.compile(FULLY_QUALFIED_NAME_PATTERN_STRING);
062    
063        // Original pattern: (([^:/]*):)?(.*)
064        private static final String PREFIXED_NAME_PATTERN_STRING = "(([^:/]*):)?(.*)";
065        private static final Pattern PREFIXED_NAME_PATTERN = Pattern.compile(PREFIXED_NAME_PATTERN_STRING);
066    
067        private final NamespaceRegistry namespaceRegistry;
068    
069        public NameValueFactory( NamespaceRegistry namespaceRegistry,
070                                 TextDecoder decoder,
071                                 ValueFactory<String> stringValueFactory ) {
072            super(PropertyType.NAME, decoder, stringValueFactory);
073            CheckArg.isNotNull(namespaceRegistry, "namespaceRegistry");
074            this.namespaceRegistry = namespaceRegistry;
075        }
076    
077        /**
078         * {@inheritDoc}
079         */
080        public Name create( String value ) {
081            return create(value, getDecoder());
082        }
083    
084        /**
085         * {@inheritDoc}
086         */
087        public Name create( String value,
088                            TextDecoder decoder ) {
089            if (value == null) return null;
090            if (decoder == null) decoder = getDecoder();
091            try {
092                // First see whether the value fits the internal pattern ...
093                Matcher matcher = FULLY_QUALIFIED_NAME_PATTERN.matcher(value);
094                if (matcher.matches()) {
095                    String namespaceUri = matcher.group(1);
096                    String localName = matcher.group(2);
097                    // Decode the parts ...
098                    namespaceUri = decoder.decode(namespaceUri);
099                    localName = decoder.decode(localName);
100                    return new BasicName(namespaceUri, localName);
101                }
102                // Second, see whether the value fits the prefixed name pattern ...
103                matcher = PREFIXED_NAME_PATTERN.matcher(value);
104                if (matcher.matches()) {
105                    String prefix = matcher.group(2);
106                    String localName = matcher.group(3);
107                    // Decode the parts ...
108                    prefix = prefix == null ? "" : decoder.decode(prefix);
109                    localName = decoder.decode(localName);
110                    // Look for a namespace match ...
111                    String namespaceUri = this.namespaceRegistry.getNamespaceForPrefix(prefix);
112                    // Fail if no namespace is found ...
113                    if (namespaceUri == null) {
114                        throw new NamespaceException(GraphI18n.noNamespaceRegisteredForPrefix.text(prefix));
115                    }
116                    return new BasicName(namespaceUri, localName);
117                }
118            } catch (NamespaceException err) {
119                throw new ValueFormatException(value, getPropertyType(),
120                                               GraphI18n.errorConvertingType.text(String.class.getSimpleName(),
121                                                                                Name.class.getSimpleName(),
122                                                                                value), err);
123            }
124            throw new ValueFormatException(value, getPropertyType(), GraphI18n.errorConvertingType.text(String.class.getSimpleName(),
125                                                                                                      Name.class.getSimpleName(),
126                                                                                                      value));
127        }
128    
129        /**
130         * {@inheritDoc}
131         */
132        public Name create( String namespaceUri,
133                            String localName ) {
134            return create(namespaceUri, localName, getDecoder());
135        }
136    
137        /**
138         * {@inheritDoc}
139         */
140        public Name create( String namespaceUri,
141                            String localName,
142                            TextDecoder decoder ) {
143            CheckArg.isNotEmpty(localName, "localName");
144            if (decoder == null) decoder = getDecoder();
145            namespaceUri = namespaceUri != null ? decoder.decode(namespaceUri.trim()) : null;
146            localName = decoder.decode(localName.trim());
147            return new BasicName(namespaceUri, localName);
148        }
149    
150        /**
151         * {@inheritDoc}
152         */
153        public Name create( int value ) {
154            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
155                                                                                                      Integer.class.getSimpleName(),
156                                                                                                      value));
157        }
158    
159        /**
160         * {@inheritDoc}
161         */
162        public Name create( long value ) {
163            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
164                                                                                                      Long.class.getSimpleName(),
165                                                                                                      value));
166        }
167    
168        /**
169         * {@inheritDoc}
170         */
171        public Name create( boolean value ) {
172            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
173                                                                                                      Boolean.class.getSimpleName(),
174                                                                                                      value));
175        }
176    
177        /**
178         * {@inheritDoc}
179         */
180        public Name create( float value ) {
181            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
182                                                                                                      Float.class.getSimpleName(),
183                                                                                                      value));
184        }
185    
186        /**
187         * {@inheritDoc}
188         */
189        public Name create( double value ) {
190            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
191                                                                                                      Double.class.getSimpleName(),
192                                                                                                      value));
193        }
194    
195        /**
196         * {@inheritDoc}
197         */
198        public Name create( BigDecimal value ) {
199            throw new ValueFormatException(value, getPropertyType(),
200                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
201                                                                            BigDecimal.class.getSimpleName(),
202                                                                            value));
203        }
204    
205        /**
206         * {@inheritDoc}
207         */
208        public Name create( Calendar value ) {
209            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
210                                                                                                      Calendar.class.getSimpleName(),
211                                                                                                      value));
212        }
213    
214        /**
215         * {@inheritDoc}
216         */
217        public Name create( Date value ) {
218            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
219                                                                                                      Date.class.getSimpleName(),
220                                                                                                      value));
221        }
222    
223        /**
224         * {@inheritDoc}
225         * 
226         * @see org.jboss.dna.graph.properties.ValueFactory#create(org.jboss.dna.graph.properties.DateTime)
227         */
228        public Name create( DateTime value ) throws ValueFormatException {
229            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
230                                                                                                      DateTime.class.getSimpleName(),
231                                                                                                      value));
232        }
233    
234        /**
235         * {@inheritDoc}
236         */
237        public Name create( Name value ) {
238            return value;
239        }
240    
241        /**
242         * {@inheritDoc}
243         */
244        public Name create( Path value ) {
245            if (value == null) return null;
246            if (!value.isAbsolute() && value.size() == 1) {
247                // A relative name of length 1 is converted to a name
248                return value.getSegment(0).getName();
249            }
250            throw new ValueFormatException(value, getPropertyType(), GraphI18n.errorConvertingType.text(Path.class.getSimpleName(),
251                                                                                                      Name.class.getSimpleName(),
252                                                                                                      value));
253        }
254    
255        /**
256         * {@inheritDoc}
257         */
258        public Name create( Reference value ) {
259            throw new ValueFormatException(value, getPropertyType(),
260                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
261                                                                            Reference.class.getSimpleName(),
262                                                                            value));
263        }
264    
265        /**
266         * {@inheritDoc}
267         */
268        public Name create( URI value ) {
269            if (value == null) return null;
270            String asciiString = value.toASCIIString();
271            // Remove any leading "./" ...
272            if (asciiString.startsWith("./") && asciiString.length() > 2) {
273                asciiString = asciiString.substring(2);
274            }
275            if (asciiString.indexOf('/') == -1) {
276                return create(asciiString);
277            }
278            throw new ValueFormatException(value, getPropertyType(), GraphI18n.errorConvertingType.text(URI.class.getSimpleName(),
279                                                                                                      Path.class.getSimpleName(),
280                                                                                                      value));
281        }
282    
283        /**
284         * {@inheritDoc}
285         * 
286         * @see org.jboss.dna.graph.properties.ValueFactory#create(java.util.UUID)
287         */
288        public Name create( UUID value ) throws IoException {
289            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
290                                                                                                      UUID.class.getSimpleName(),
291                                                                                                      value));
292        }
293    
294        /**
295         * {@inheritDoc}
296         */
297        public Name create( byte[] value ) {
298            // First attempt to create a string from the value, then a long from the string ...
299            return create(getStringValueFactory().create(value));
300        }
301    
302        /**
303         * {@inheritDoc}
304         * 
305         * @see org.jboss.dna.graph.properties.ValueFactory#create(org.jboss.dna.graph.properties.Binary)
306         */
307        public Name create( Binary value ) throws ValueFormatException, IoException {
308            // First create a string and then create the boolean from the string value ...
309            return create(getStringValueFactory().create(value));
310        }
311    
312        /**
313         * {@inheritDoc}
314         */
315        public Name create( InputStream stream,
316                            long approximateLength ) throws IoException {
317            // First attempt to create a string from the value, then a double from the string ...
318            return create(getStringValueFactory().create(stream, approximateLength));
319        }
320    
321        /**
322         * {@inheritDoc}
323         */
324        public Name create( Reader reader,
325                            long approximateLength ) throws IoException {
326            // First attempt to create a string from the value, then a double from the string ...
327            return create(getStringValueFactory().create(reader, approximateLength));
328        }
329    
330        /**
331         * <p>
332         * {@inheritDoc}
333         * </p>
334         * 
335         * @see org.jboss.dna.graph.properties.NameFactory#getNamespaceRegistry()
336         */
337        public NamespaceRegistry getNamespaceRegistry() {
338            return namespaceRegistry;
339        }
340    
341        /**
342         * {@inheritDoc}
343         */
344        @Override
345        protected Name[] createEmptyArray( int length ) {
346            return new Name[length];
347        }
348    }