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