001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.xml;
014
015import java.util.*;
016import java.util.concurrent.*;
017
018import org.apache.juneau.*;
019import org.apache.juneau.annotation.*;
020import org.apache.juneau.internal.*;
021
022/**
023 * Represents a simple namespace mapping between a simple name and URI.
024 *
025 * <p>
026 * In general, the simple name will be used as the XML prefix mapping unless there are conflicts or prefix re-mappings
027 * in the serializer.
028 */
029@Bean(sort=true)
030public final class Namespace {
031
032   private static final ConcurrentHashMap<String,Namespace> CACHE = new ConcurrentHashMap<>();
033
034
035   /**
036    * Create a {@link Namespace} with the specified name and URI.
037    *
038    * <p>
039    * Previously-encountered name/uri pairs return a cached copy.
040    *
041    * @param name The namespace name.  See {@link Namespace#getName()}.
042    * @param uri The namespace URI.  See {@link Namespace#getUri()}.
043    * @return The namespace object.
044    */
045   public static Namespace create(String name, String uri) {
046      String key = name + ":" + uri;
047      Namespace n = CACHE.get(key);
048      if (n == null) {
049         n = new Namespace(key, name, uri);
050         Namespace n2 = CACHE.putIfAbsent(key, n);
051         return (n2 == null ? n : n2);
052      }
053      return n;
054   }
055
056   /**
057    * Create a {@link Namespace} from a <js>"name:uri"</js> string pair.
058    *
059    * @param key The key/pair string.
060    * @return The namespace object.
061    */
062   public static Namespace create(String key) {
063      Namespace n = CACHE.get(key);
064      if (n != null)
065         return n;
066      int i = key.indexOf(':');
067      if (i == -1)
068         return create(key, null);
069      if (key.startsWith("http://") || key.startsWith("https://"))
070         return create(null, key);
071      return create(key.substring(0, i).trim(), key.substring(i+1).trim());
072   }
073
074   /**
075    * Converts the specified object into a {@link Namespace} object.
076    *
077    * <p>
078    * Can be any of following types:
079    * <ul>
080    *    <li>A {@link Namespace} object
081    *    <li>A string containing a name/value pair of the form <js>"name:uri"</js>.
082    * </ul>
083    *
084    * @param o The input.
085    * @return The namespace object, or <jk>null</jk> if the input was <jk>null</jk> or an empty JSON object.
086    */
087   public static Namespace create(Object o) {
088      if (o == null)
089         return null;
090      if (o instanceof Namespace)
091         return (Namespace)o;
092      if (o instanceof CharSequence)
093         return create(o.toString());
094      throw new FormattedRuntimeException("Invalid object type passed to Namespace.create(Object):  ''{0}''", o.getClass());
095   }
096
097   /**
098    * Converts the specified object into an array of {@link Namespace} object.
099    *
100    * <p>
101    * Can be any of following types:
102    * <ul>
103    *    <li>A {@link Namespace} array
104    *    <li>A comma-delimited string with key/value pairs of the form <js>"name:uri"</js>.
105    *    <li>A <code>Collection</code> containing any of object that can be passed to {@link #createArray(Object)}.
106    * </ul>
107    *
108    * @param o The input.
109    * @return The namespace objects, or <jk>null</jk> if the input was <jk>null</jk> or an empty JSON object.
110    */
111   @SuppressWarnings("rawtypes")
112   public static Namespace[] createArray(Object o) {
113
114      if (o instanceof Namespace[])
115         return (Namespace[])o;
116
117      if (o instanceof CharSequence) {
118         String[] ss = StringUtils.split(o.toString());
119         Namespace[] n = new Namespace[ss.length];
120         for (int i = 0; i < ss.length; i++)
121            n[i] = create(ss[i]);
122         return n;
123      }
124
125      if (o instanceof Collection) {
126         Collection c = (Collection)o;
127         Namespace[] n = new Namespace[c.size()];
128         int i = 0;
129         for (Object o2 : c){
130            if (o2 instanceof Namespace)
131               n[i] = (Namespace)o2;
132            else if (o2 instanceof CharSequence)
133               n[i] = create(o2.toString());
134            else
135               throw new FormattedRuntimeException("Invalid type passed to NamespaceFactory.createArray: ''{0}''", o);
136         }
137         return n;
138      }
139
140      throw new FormattedRuntimeException("Invalid type passed to NamespaceFactory.createArray: ''{0}''", o);
141   }
142
143
144   final String key, name, uri;
145
146   /**
147    * Constructor.
148    *
149    * @param name The short name of this schema.
150    * @param uri The URI of this schema.
151    */
152   private Namespace(String key, String name, String uri) {
153      this.key = key;
154      this.name = name;
155      this.uri = uri;
156   }
157
158   /**
159    * Returns the namespace name.
160    *
161    * @return The namespace name.
162    */
163   public String getName() {
164      return name;
165   }
166
167   /**
168    * Returns the namespace URI.
169    *
170    * @return The namespace URI.
171    */
172   public String getUri() {
173      return uri;
174   }
175
176   @Override /* Object */
177   public String toString() {
178      return key;
179   }
180}