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.internal; 019 020import static java.util.Collections.emptyList; 021import static java.util.Collections.emptySet; 022import static org.apiguardian.api.API.Status.INTERNAL; 023import static org.tquadrat.foundation.lang.CommonConstants.CR; 024import static org.tquadrat.foundation.lang.CommonConstants.EMPTY_STRING; 025import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 026import static org.tquadrat.foundation.lang.Objects.requireNotEmptyArgument; 027import static org.tquadrat.foundation.util.StringUtils.isNotEmpty; 028import static org.tquadrat.foundation.xml.builder.XMLBuilderUtils.getElementNameValidator; 029import static org.tquadrat.foundation.xml.builder.spi.SGMLPrinter.repeat; 030 031import java.util.ArrayList; 032import java.util.Collection; 033import java.util.Map; 034import java.util.Optional; 035import java.util.StringJoiner; 036 037import org.apiguardian.api.API; 038import org.tquadrat.foundation.annotation.ClassVersion; 039import org.tquadrat.foundation.util.LazyList; 040import org.tquadrat.foundation.xml.builder.Namespace; 041import org.tquadrat.foundation.xml.builder.ProcessingInstruction; 042import org.tquadrat.foundation.xml.builder.spi.AttributeSupport; 043import org.tquadrat.foundation.xml.builder.spi.Element; 044import org.tquadrat.foundation.xml.builder.spi.InvalidXMLNameException; 045 046/** 047 * The implementation for the interface 048 * {@link ProcessingInstruction}. 049 * 050 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 051 * @version $Id: ProcessingInstructionImpl.java 1101 2024-02-18 00:18:48Z tquadrat $ 052 * @since 0.0.5 053 * 054 * @UMLGraph.link 055 */ 056@ClassVersion( sourceVersion = "$Id: ProcessingInstructionImpl.java 1101 2024-02-18 00:18:48Z tquadrat $" ) 057@API( status = INTERNAL, since = "0.0.5" ) 058public final class ProcessingInstructionImpl implements ProcessingInstruction 059{ 060 /*------------*\ 061 ====** Attributes **======================================================= 062 \*------------*/ 063 /** 064 * The attributes for the processing instruction. 065 */ 066 @SuppressWarnings( "UseOfConcreteClass" ) 067 private final AttributeSupport m_Attributes; 068 069 /** 070 * The data for the processing instruction. 071 */ 072 private final LazyList<String> m_Data = LazyList.use( false, ArrayList::new ); 073 074 /** 075 * The name for this processing instruction. 076 */ 077 private final String m_ElementName; 078 079 /** 080 * The parent for this processing instruction. 081 */ 082 private Element m_Parent = null; 083 084 /*--------------*\ 085 ====** Constructors **===================================================== 086 \*--------------*/ 087 /** 088 * Creates a new {@code ProcessingInstruction} instance. 089 * 090 * @param name The name for this processing instruction. 091 */ 092 public ProcessingInstructionImpl( final String name ) { this( name, null ); } 093 094 /** 095 * Creates the {@code ProcessingInstruction} instance for the XML header. 096 */ 097 @SuppressWarnings( "ThisEscapedInObjectConstruction" ) 098 public ProcessingInstructionImpl() 099 { 100 m_ElementName = "xml"; 101 102 final var attributeSequence = new String [] {"version", "encoding", "standalone"}; 103 104 m_Attributes = new AttributeSupport( this, true ); 105 m_Attributes.registerAttributes( attributeSequence ); 106 m_Attributes.registerSequence( attributeSequence ); 107 } // ProcessingInstruction() 108 109 /** 110 * Creates a new {@code ProcessingInstruction} instance. 111 * 112 * @param name The name for this processing instruction. 113 * @param data The data for the processing instruction; can be 114 * {@code null}. 115 */ 116 @SuppressWarnings( "ThisEscapedInObjectConstruction" ) 117 public ProcessingInstructionImpl( final String name, final CharSequence data ) 118 { 119 if( !getElementNameValidator().test( requireNotEmptyArgument( name, "name" ) ) ) 120 { 121 throw new InvalidXMLNameException( name ); 122 } 123 m_ElementName = name; 124 if( isNotEmpty( data ) ) m_Data.add( data.toString() ); 125 126 m_Attributes = new AttributeSupport( this, false ); 127 } // ProcessingInstruction() 128 129 /*---------*\ 130 ====** Methods **========================================================== 131 \*---------*/ 132 /** 133 * {@inheritDoc} 134 */ 135 @Override 136 public final ProcessingInstruction addData( final CharSequence data ) 137 { 138 m_Data.add( requireNotEmptyArgument( data, "data" ).toString() ); 139 140 //---* Done *---------------------------------------------------------- 141 return this; 142 } // addData() 143 144 /** 145 * {@inheritDoc} 146 */ 147 @Override 148 public final Optional<String> getAttribute( final String name ) { return m_Attributes.getAttribute( name ); } 149 150 /** 151 * {@inheritDoc} 152 */ 153 @Override 154 public final Map<String,String> getAttributes() { return m_Attributes.getAttributes(); } 155 156 /** 157 * {@inheritDoc} 158 */ 159 @Override 160 public final Collection<? extends Element> getChildren() { return emptyList(); } 161 162 /** 163 * {@inheritDoc} 164 */ 165 @Override 166 public final String getElementName() { return m_ElementName; } 167 168 /** 169 * {@inheritDoc} 170 */ 171 @Override 172 public final Collection<Namespace> getNamespaces() { return emptySet(); } 173 174 /** 175 * {@inheritDoc} 176 */ 177 @Override 178 public final Optional<Element> getParent() { return Optional.ofNullable( m_Parent ); } 179 180 /** 181 * {@inheritDoc} 182 */ 183 @Override 184 public final boolean hasChildren() { return false; } 185 186 /** 187 * {@inheritDoc} 188 */ 189 @Override 190 public final boolean isBlock() { return true; } 191 192 /** 193 * <p>{@inheritDoc}</p> 194 * <p>The given attribute name is validated using the method that is 195 * provided by 196 * {@link org.tquadrat.foundation.xml.builder.XMLBuilderUtils#getAttributeNameValidator()}.</p> 197 */ 198 @Override 199 public final ProcessingInstruction setAttribute( final String name, final CharSequence value, final Optional<? extends CharSequence> append ) throws IllegalArgumentException 200 { 201 m_Attributes.setAttribute( name, value, append ); 202 203 //---* Done *---------------------------------------------------------- 204 return this; 205 } // setAttribute() 206 207 /** 208 * {@inheritDoc} 209 */ 210 @Override 211 public final <E extends Element> void setParent( final E parent ) { m_Parent = requireNonNullArgument( parent, "parent" ); } 212 213 /** 214 * {@inheritDoc} 215 */ 216 @Override 217 public final String toString( final int indentationLevel, final boolean prettyPrint ) 218 { 219 final var text = new StringBuilder( 256 ); 220 221 //---* Determine the filler *------------------------------------------ 222 final var filler = prettyPrint ? "\n" + repeat( indentationLevel, m_ElementName.length() + 2 ) : EMPTY_STRING; 223 224 //---* Add the data *-------------------------------------------------- 225 if( m_Data.isPresent() ) 226 { 227 final var joiner = new StringJoiner( filler + " ", " ", EMPTY_STRING ); 228 for( final var data : m_Data ) joiner.add( data ); 229 text.append( joiner ); 230 } 231 232 //---* Add the attributes *-------------------------------------------- 233 getAttributes().forEach( (key,value) -> 234 { 235 if( !text.isEmpty() ) text.append( filler ); 236 text.append( ' ' ) 237 .append( key ) 238 .append( "='") 239 .append( value ) 240 .append( '\'' ); 241 } ); 242 243 //---* Calculate the indentation *------------------------------------- 244 final var indentation = prettyPrint && (indentationLevel > 0) ? repeat( indentationLevel ) : EMPTY_STRING; 245 final var newLine = prettyPrint ? CR : EMPTY_STRING; 246 247 //---* Render the element *-------------------------------------------- 248 final var retValue = STR.""" 249 \{indentation}<?\{m_ElementName}\{text}?>\{newLine}\ 250 """; 251 252 //---* Done *---------------------------------------------------------- 253 return retValue; 254 } // toString() 255 256 /** 257 * {@inheritDoc} 258 */ 259 @Override 260 public final String toString() { return toString( 0, true ); } 261} 262// class ProcessingInstruction 263 264/* 265 * End of File 266 */