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 static org.apache.juneau.internal.ClassUtils.*;
016import java.util.*;
017import java.util.concurrent.*;
018
019import org.apache.juneau.*;
020import org.apache.juneau.annotation.*;
021import org.apache.juneau.common.internal.*;
022
023/**
024 * Represents a simple namespace mapping between a simple name and URI.
025 *
026 * <p>
027 * In general, the simple name will be used as the XML prefix mapping unless there are conflicts or prefix re-mappings
028 * in the serializer.
029 *
030 * <h5 class='section'>See Also:</h5><ul>
031 *    <li class='link'><a class="doclink" href="../../../../index.html#jm.XmlDetails">XML Details</a>
032 * </ul>
033 */
034@Bean(sort=true)
035public final class Namespace {
036
037   private static final ConcurrentHashMap<String,Namespace> CACHE = new ConcurrentHashMap<>();
038
039
040   /**
041    * Create a {@link Namespace} with the specified name and URI.
042    *
043    * <p>
044    * Previously-encountered name/uri pairs return a cached copy.
045    *
046    * @param name The namespace name.  See {@link Namespace#getName()}.
047    * @param uri The namespace URI.  See {@link Namespace#getUri()}.
048    * @return The namespace object.
049    */
050   public static Namespace of(String name, String uri) {
051      String key = name + ":" + uri;
052      Namespace n = CACHE.get(key);
053      if (n == null) {
054         n = new Namespace(key, name, uri);
055         Namespace n2 = CACHE.putIfAbsent(key, n);
056         return (n2 == null ? n : n2);
057      }
058      return n;
059   }
060
061   /**
062    * Create a {@link Namespace} from a <js>"name:uri"</js> string pair.
063    *
064    * @param key The key/pair string.
065    * @return The namespace object.
066    */
067   public static Namespace of(String key) {
068      Namespace n = CACHE.get(key);
069      if (n != null)
070         return n;
071      int i = key.indexOf(':');
072      if (i == -1)
073         return of(key, null);
074      if (key.startsWith("http://") || key.startsWith("https://"))
075         return of(null, key);
076      return of(key.substring(0, i).trim(), key.substring(i+1).trim());
077   }
078
079   /**
080    * Converts the specified object into a {@link Namespace} object.
081    *
082    * <p>
083    * Can be any of following types:
084    * <ul>
085    *    <li>A {@link Namespace} object
086    *    <li>A string containing a name/value pair of the form <js>"name:uri"</js>.
087    * </ul>
088    *
089    * @param o The input.
090    * @return The namespace object, or <jk>null</jk> if the input was <jk>null</jk> or an empty JSON object.
091    */
092   public static Namespace create(Object o) {
093      if (o == null)
094         return null;
095      if (o instanceof Namespace)
096         return (Namespace)o;
097      if (o instanceof CharSequence)
098         return of(o.toString());
099      throw new BasicRuntimeException("Invalid object type passed to Namespace.create(Object):  ''{0}''", className(o));
100   }
101
102   /**
103    * Converts the specified object into an array of {@link Namespace} object.
104    *
105    * <p>
106    * Can be any of following types:
107    * <ul>
108    *    <li>A {@link Namespace} array
109    *    <li>A comma-delimited string with key/value pairs of the form <js>"name:uri"</js>.
110    *    <li>A <c>Collection</c> containing any of object that can be passed to {@link #createArray(Object)}.
111    * </ul>
112    *
113    * @param o The input.
114    * @return The namespace objects, or <jk>null</jk> if the input was <jk>null</jk> or an empty JSON object.
115    */
116   @SuppressWarnings("rawtypes")
117   public static Namespace[] createArray(Object o) {
118
119      if (o instanceof Namespace[])
120         return (Namespace[])o;
121
122      if (o instanceof String[]) {
123         String[] ss = (String[])o;
124         Namespace[] n = new Namespace[ss.length];
125         for (int i = 0; i < ss.length; i++)
126            n[i] = create(ss[i]);
127         return n;
128      }
129
130      if (o instanceof CharSequence) {
131         String[] ss = StringUtils.split(o.toString());
132         Namespace[] n = new Namespace[ss.length];
133         for (int i = 0; i < ss.length; i++)
134            n[i] = create(ss[i]);
135         return n;
136      }
137
138      if (o instanceof Collection) {
139         Collection c = (Collection)o;
140         Namespace[] n = new Namespace[c.size()];
141         int i = 0;
142         for (Object o2 : c){
143            if (o2 instanceof Namespace)
144               n[i++] = (Namespace)o2;
145            else if (o2 instanceof CharSequence)
146               n[i++] = create(o2.toString());
147            else
148               throw new BasicRuntimeException("Invalid type passed to NamespaceFactory.createArray: ''{0}''", o);
149         }
150         return n;
151      }
152
153      throw new BasicRuntimeException("Invalid type passed to NamespaceFactory.createArray: ''{0}''", o);
154   }
155
156
157   final String key, name, uri;
158
159   /**
160    * Constructor.
161    *
162    * @param name The short name of this schema.
163    * @param uri The URI of this schema.
164    */
165   private Namespace(String key, String name, String uri) {
166      this.key = key;
167      this.name = name;
168      this.uri = uri;
169   }
170
171   /**
172    * Returns the namespace name.
173    *
174    * @return The namespace name.
175    */
176   public String getName() {
177      return name;
178   }
179
180   /**
181    * Returns the namespace URI.
182    *
183    * @return The namespace URI.
184    */
185   public String getUri() {
186      return uri;
187   }
188
189   @Override /* Object */
190   public String toString() {
191      return key;
192   }
193}