001/* 002 * ============================================================================ 003 * Copyright © 2002-2024 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * Licensed to the public under the agreements of the GNU Lesser General Public 007 * License, version 3.0 (the "License"). You may obtain a copy of the License at 008 * 009 * http://www.gnu.org/licenses/lgpl.html 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, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations 015 * under the License. 016 */ 017 018package org.tquadrat.foundation.xml.builder; 019 020import static java.lang.String.format; 021import static org.apiguardian.api.API.Status.STABLE; 022import static org.tquadrat.foundation.lang.Objects.hash; 023import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 024import static org.tquadrat.foundation.lang.Objects.requireNotEmptyArgument; 025import static org.tquadrat.foundation.xml.builder.XMLBuilderUtils.getPrefixValidator; 026 027import java.io.Serial; 028import java.io.Serializable; 029import java.net.URI; 030import java.net.URISyntaxException; 031import java.util.Optional; 032 033import org.apiguardian.api.API; 034import org.tquadrat.foundation.annotation.ClassVersion; 035import org.tquadrat.foundation.xml.builder.spi.InvalidXMLNameException; 036 037/** 038 * The definition of an XML namespace entry. 039 * 040 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 041 * @version $Id: Namespace.java 1101 2024-02-18 00:18:48Z tquadrat $ 042 * @since 0.0.5 043 * 044 * @UMLGraph.link 045 */ 046@ClassVersion( sourceVersion = "$Id: Namespace.java 1101 2024-02-18 00:18:48Z tquadrat $" ) 047@API( status = STABLE, since = "0.0.5" ) 048public final class Namespace implements Serializable, Comparable<Namespace> 049{ 050 /*-----------*\ 051 ====** Constants **======================================================== 052 \*-----------*/ 053 /** 054 * An empty array of {@code Namespace} objects. 055 */ 056 public static final Namespace [] EMPTY_Namespace_ARRAY = new Namespace [0]; 057 058 /** 059 * The keyword for a namespace definition: {@value}. 060 */ 061 public static final String KEYWORD = "xmlns"; 062 063 /*------------*\ 064 ====** Attributes **======================================================= 065 \*------------*/ 066 /** 067 * The namespace identifier. 068 * 069 * @serial 070 */ 071 private final URI m_Identifier; 072 073 /** 074 * The namespace prefix. 075 * 076 * @serial 077 */ 078 @SuppressWarnings( "OptionalUsedAsFieldOrParameterType" ) 079 private final Optional<String> m_Prefix; 080 081 /*------------------------*\ 082 ====** Static Initialisations **=========================================== 083 \*------------------------*/ 084 /** 085 * The serial version UID for objects of this class: {@value}. 086 * 087 * @hidden 088 */ 089 @Serial 090 private static final long serialVersionUID = 1L; 091 092 /*--------------*\ 093 ====** Constructors **===================================================== 094 \*--------------*/ 095 /** 096 * Creates a new {@code Namespace} instance without a prefix. 097 * 098 * @param identifier The namespace identifier. 099 */ 100 public Namespace( final URI identifier ) 101 { 102 this( Optional.empty(), identifier ); 103 } // Namespace() 104 105 /** 106 * Creates a new {@code Namespace} instance without a prefix. 107 * 108 * @param identifier The namespace identifier. 109 * @throws URISyntaxException The provided URI String is invalid. 110 */ 111 public Namespace( final String identifier ) throws URISyntaxException 112 { 113 this( Optional.empty(), new URI( requireNotEmptyArgument( identifier, "identifier" ) ) ); 114 } // Namespace() 115 116 /** 117 * Creates a new {@code Namespace} instance.<br> 118 * <br>The given prefix is validated using the method that is 119 * provided by 120 * {@link org.tquadrat.foundation.xml.builder.XMLBuilderUtils#getPrefixValidator()}. 121 * 122 * @param prefix The namespace prefix. 123 * @param identifier The namespace identifier. 124 */ 125 public Namespace( final String prefix, final URI identifier ) 126 { 127 this( Optional.of( requireNotEmptyArgument( prefix, "prefix" ) ), identifier ); 128 } // Namespace() 129 130 /** 131 * Creates a new {@code Namespace} instance.<br> 132 * <br>The given prefix is validated using the method that is 133 * provided by 134 * {@link org.tquadrat.foundation.xml.builder.XMLBuilderUtils#getPrefixValidator()}. 135 * 136 * @param prefix The namespace prefix. 137 * @param identifier The namespace identifier. 138 * @throws URISyntaxException The provided URI String is invalid. 139 */ 140 public Namespace( final String prefix, final String identifier ) throws URISyntaxException 141 { 142 this( Optional.of( requireNotEmptyArgument( prefix, "prefix" ) ), new URI( requireNotEmptyArgument( identifier, "identifier" ) ) ); 143 } // Namespace() 144 145 /** 146 * Creates a new {@code Namespace} instance.<br> 147 * <br>The given prefix is validated using the method that is 148 * provided by 149 * {@link org.tquadrat.foundation.xml.builder.XMLBuilderUtils#getPrefixValidator()}. 150 * 151 * @param prefix The namespace prefix. 152 * @param identifier The namespace identifier. 153 */ 154 @SuppressWarnings( "OptionalUsedAsFieldOrParameterType" ) 155 private Namespace( final Optional<String> prefix, final URI identifier ) 156 { 157 if( prefix.isPresent() && !getPrefixValidator().test( prefix.get() ) ) 158 { 159 throw new InvalidXMLNameException( prefix.get() ); 160 } 161 162 m_Prefix = prefix; 163 m_Identifier = requireNonNullArgument( identifier, "identifier" ); 164 } // Namespace() 165 166 /*---------*\ 167 ====** Methods **========================================================== 168 \*---------*/ 169 /** 170 * {@inheritDoc} 171 */ 172 @SuppressWarnings( "UseOfConcreteClass" ) 173 @Override 174 public final int compareTo( final Namespace o ) 175 { 176 final var retValue = m_Prefix.isPresent() 177 ? (o.m_Prefix.isPresent() ? Integer.signum( m_Prefix.orElseThrow().compareTo( o.m_Prefix.orElseThrow() ) ) : 1) 178 : (o.m_Prefix.isPresent() ? -1 : 0); 179 180 //---* Done *---------------------------------------------------------- 181 return retValue; 182 } // compareTo() 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override 188 public final boolean equals( final Object obj ) 189 { 190 var retValue = this == obj; 191 if( !retValue && obj instanceof final Namespace other ) 192 { 193 retValue = m_Prefix.equals( other.m_Prefix ) && m_Identifier.equals( other.m_Identifier ); 194 } 195 196 //---* Done *---------------------------------------------------------- 197 return retValue; 198 } 199 200 /** 201 * Returns the namespace identifier. 202 * 203 * @return The namespace identifier. 204 */ 205 public final URI getIdentifier() { return m_Identifier; } 206 207 /** 208 * Returns the namespace prefix. 209 * 210 * @return An instance of 211 * {@link Optional} 212 * that holds the namespace prefix. 213 */ 214 public final Optional<String> getPrefix() { return m_Prefix; } 215 216 /** 217 * {@inheritDoc} 218 */ 219 @Override 220 public final int hashCode() { return hash( m_Prefix, m_Identifier ); } 221 222 /** 223 * {@inheritDoc} 224 */ 225 @Override 226 public final String toString() 227 { 228 final var retValue = m_Prefix 229 .map( p -> format( "%s:%s=\"%s\"", KEYWORD, p, m_Identifier.toString() ) ) 230 .orElseGet( () -> format( "%s=\"%s\"", KEYWORD, m_Identifier.toString() ) ); 231 232 //---* Done *---------------------------------------------------------- 233 return retValue; 234 } // toString() 235} 236// class Namespace 237 238/* 239 * End of File 240 */