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