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 <c>Collection</c> 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 String[]) {
118         String[] ss = (String[])o;
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 CharSequence) {
126         String[] ss = StringUtils.split(o.toString());
127         Namespace[] n = new Namespace[ss.length];
128         for (int i = 0; i < ss.length; i++)
129            n[i] = create(ss[i]);
130         return n;
131      }
132
133      if (o instanceof Collection) {
134         Collection c = (Collection)o;
135         Namespace[] n = new Namespace[c.size()];
136         int i = 0;
137         for (Object o2 : c){
138            if (o2 instanceof Namespace)
139               n[i] = (Namespace)o2;
140            else if (o2 instanceof CharSequence)
141               n[i] = create(o2.toString());
142            else
143               throw new FormattedRuntimeException("Invalid type passed to NamespaceFactory.createArray: ''{0}''", o);
144         }
145         return n;
146      }
147
148      throw new FormattedRuntimeException("Invalid type passed to NamespaceFactory.createArray: ''{0}''", o);
149   }
150
151
152   final String key, name, uri;
153
154   /**
155    * Constructor.
156    *
157    * @param name The short name of this schema.
158    * @param uri The URI of this schema.
159    */
160   private Namespace(String key, String name, String uri) {
161      this.key = key;
162      this.name = name;
163      this.uri = uri;
164   }
165
166   /**
167    * Returns the namespace name.
168    *
169    * @return The namespace name.
170    */
171   public String getName() {
172      return name;
173   }
174
175   /**
176    * Returns the namespace URI.
177    *
178    * @return The namespace URI.
179    */
180   public String getUri() {
181      return uri;
182   }
183
184   @Override /* Object */
185   public String toString() {
186      return key;
187   }
188}