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