001/*
002 * ============================================================================
003 * Copyright © 2002-2023 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 org.apiguardian.api.API.Status.STABLE;
021import static org.tquadrat.foundation.lang.CommonConstants.XMLATTRIBUTE_Id;
022import static org.tquadrat.foundation.lang.Objects.nonNull;
023import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument;
024import static org.tquadrat.foundation.lang.Objects.requireNotEmptyArgument;
025import static org.tquadrat.foundation.util.StringUtils.isNotEmpty;
026import static org.tquadrat.foundation.xml.builder.XMLBuilderUtils.getNMTokenValidator;
027
028import java.net.URI;
029import java.net.URISyntaxException;
030import java.time.Instant;
031import java.time.LocalDate;
032import java.time.LocalDateTime;
033import java.time.ZonedDateTime;
034import java.util.Optional;
035import java.util.Set;
036
037import org.apiguardian.api.API;
038import org.tquadrat.foundation.annotation.ClassVersion;
039import org.tquadrat.foundation.exception.IllegalOperationException;
040import org.tquadrat.foundation.xml.builder.internal.XMLElementImpl;
041import org.tquadrat.foundation.xml.builder.spi.Element;
042
043/**
044 *  <p>{@summary The definition of an XMLElement.}</p>
045 *  <p>The default implementation of the methods
046 *  {@link #addCDATA(CharSequence)},
047 *  {@link #addChild(XMLElement)},
048 *  {@link #addComment(CharSequence)},
049 *  {@link #addText(CharSequence)},
050 *  {@link #setAttribute(String, CharSequence, Optional)},
051 *  {@link #setNamespace(String)},
052 *  {@link #setNamespace(URI)},
053 *  {@link #setNamespace(String,String)},
054 *  {@link #setNamespace(String,URI)},
055 *  and
056 *  {@link #setNamespace(Namespace)}
057 *  will always throw an
058 *  {@link IllegalOperationException};
059 *  classes that implement this interface will have to provide appropriate
060 *  implementations for these methods if they want to support the respective
061 *  feature.</p>
062 *
063 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
064 *  @version $Id: XMLElement.java 1071 2023-09-30 01:49:32Z tquadrat $
065 *  @since 0.0.5
066 *
067 *  @UMLGraph.link
068 */
069@SuppressWarnings( "ClassWithTooManyMethods" )
070@ClassVersion( sourceVersion = "$Id: XMLElement.java 1071 2023-09-30 01:49:32Z tquadrat $" )
071@API( status = STABLE, since = "0.0.5" )
072public sealed interface XMLElement extends Element
073    permits XMLElementImpl
074{
075        /*---------------*\
076    ====** Inner Classes **====================================================
077        \*---------------*/
078    /**
079     *  The flags that are used to configure a new instance of
080     *  {@code XMLElement} (respectively an instance of an implementation of
081     *  this interface.
082     *
083     *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
084     *  @version $Id: XMLElement.java 1071 2023-09-30 01:49:32Z tquadrat $
085     *  @since 0.0.5
086     *
087     *  @UMLGraph.link
088     */
089    @SuppressWarnings( "NewClassNamingConvention" )
090    @ClassVersion( sourceVersion = "$Id: XMLElement.java 1071 2023-09-30 01:49:32Z tquadrat $" )
091    @API( status = STABLE, since = "0.1.0" )
092    public static enum Flags
093    {
094        /**
095         *  The element allows children.
096         */
097        ALLOWS_CHILDREN,
098
099        /**
100         *  The element allows text.
101         */
102        ALLOWS_TEXT,
103
104        /**
105         *  Only valid attributes can be added to the element (if not set, any
106         *  attribut can be added).
107         */
108        VALIDATES_ATTRIBUTES,
109
110        /**
111         *  Only valid children can be added; this implies that
112         *  {@link #ALLOWS_CHILDREN}
113         *  is set; if the further is set, but this one not, any child can be
114         *  added.
115         */
116        VALIDATES_CHILDREN
117    }
118    //  enum Flags
119
120        /*-----------*\
121    ====** Constants **========================================================
122        \*-----------*/
123    /**
124     *  An empty array of {@code XMLElement} objects.
125     */
126    public static final XMLElement [] EMPTY_XMLElement_ARRAY = new XMLElement [0];
127
128    /**
129     *  The indicator that values for an attribute should not be appended.
130     *
131     *  @see #setAttribute(String, CharSequence, Optional)
132     */
133    @SuppressWarnings( "OptionalUsedAsFieldOrParameterType" )
134    public static final Optional<? extends CharSequence> NO_APPEND = Optional.empty();
135
136        /*---------*\
137    ====** Methods **==========================================================
138        \*---------*/
139    /**
140     *  <p>{@summary Adds the provided {@code boolean} value as a {@code CDATA}
141     *  element to this XML element.}</p>
142     *  <p>To convert the boolean to text, the method
143     *  {@link Boolean#toString(boolean)}
144     *  is used.</p>
145     *
146     *  @param  flag    The value.
147     *  @return This instance.
148     *  @throws IllegalOperationException    No text allowed for this element.
149     */
150    @SuppressWarnings( "UnusedReturnValue" )
151    public default XMLElement addCDATA( final boolean flag ) throws IllegalOperationException { return addCDATA( Boolean.toString( flag ) ); }
152
153    /**
154     *  <p>{@summary Adds the provided
155     *  {@link Boolean}
156     *  instance as a {@code CDATA} element to this XML element.}</p>
157     *  <p>To convert the boolean to text, the method
158     *  {@link Boolean#toString()}
159     *  is used.</p>
160     *
161     *  @param  flag    The value.
162     *  @return This instance.
163     *  @throws IllegalOperationException    No text allowed for this element.
164     */
165    @SuppressWarnings( "UnusedReturnValue" )
166    public default XMLElement addCDATA( final Boolean flag ) throws IllegalOperationException { return addCDATA( requireNonNullArgument( flag, "flag" ).toString() ); }
167
168    /**
169     *  <p>{@summary Adds the provided {@code char} value as a {@code CDATA}
170     *  element to this XML element.}</p>
171     *  <p>To convert the character to text, the method
172     *  {@link Character#toString(char)}
173     *  is used.</p>
174     *
175     *  @param  c   The character.
176     *  @return This instance.
177     *  @throws IllegalOperationException    No text allowed for this element.
178     */
179    @SuppressWarnings( "UnusedReturnValue" )
180    public default XMLElement addCDATA( final char c ) throws IllegalOperationException { return addCDATA( Character.toString( c ) ); }
181
182    /**
183     *  <p>{@summary Adds the provided
184     *  {@link Character}
185     *  instance as a {@code CDATA} element to this XML element.}</p>
186     *  <p>To convert the character to text, the method
187     *  {@link Character#toString()}
188     *  is used.</p>
189     *
190     *  @param  c   The character.
191     *  @return This instance.
192     *  @throws IllegalOperationException    No text allowed for this element.
193     */
194    @SuppressWarnings( "UnusedReturnValue" )
195    public default XMLElement addCDATA( final Character c ) throws IllegalOperationException { return addCDATA( requireNonNullArgument( c, "c" ).toString() ); }
196
197    /**
198     *  Adds a {@code CDATA} element to this XML element.
199     *
200     *  @param  text    The text.
201     *  @return This instance.
202     *  @throws IllegalOperationException    No text allowed for this element.
203     */
204    public default XMLElement addCDATA( final CharSequence text ) throws IllegalOperationException
205    {
206        throw new IllegalOperationException( "No text allowed for element '%1$s'".formatted( getElementName() ) );
207    }   //  addCDATA()
208
209    /**
210     *  <p>{@summary Adds the provided {@code double} value as a {@code CDATA}
211     *  element to this XML element.}</p>
212     *  <p>To convert the number to text, the method
213     *  {@link Double#toString(double)}
214     *  is used.</p>
215     *
216     *  @param  number  The number.
217     *  @return This instance.
218     *  @throws IllegalOperationException    No text allowed for this element.
219     */
220    @SuppressWarnings( "UnusedReturnValue" )
221    public default XMLElement addCDATA( final double number ) throws IllegalOperationException { return addCDATA( Double.toString( number ) ); }
222
223    /**
224     *  <p>{@summary Adds the provided {@code enum} instance as a {@code CDATA}
225     *  element to this XML element.}</p>
226     *  <p>To convert the value to text, the method
227     *  {@link Enum#name()}
228     *  is used.</p>
229     *
230     *  @param  <E> The type of the {@code enum} value.
231     *  @param  value   The value.
232     *  @return This instance.
233     *  @throws IllegalOperationException    No text allowed for this element.
234     */
235    @SuppressWarnings( "UnusedReturnValue" )
236    public default <E extends Enum<E>> XMLElement addCDATA( final E value ) throws IllegalOperationException { return addCDATA( requireNonNullArgument( value, "value" ).name() ); }
237
238    /**
239     *  <p>{@summary Adds the provided
240     *  {@link Instant}
241     *  instance as a {@code CDATA} element to this XML element.}</p>
242     *  <p>To convert the date to text, the method
243     *  {@link Instant#toString()}
244     *  is used.</p>
245     *
246     *  @param  date    The date.
247     *  @return This instance.
248     *  @throws IllegalOperationException    No text allowed for this element.
249     */
250    @SuppressWarnings( "UnusedReturnValue" )
251    public default XMLElement addCDATA( final Instant date ) throws IllegalOperationException { return addCDATA( requireNonNullArgument( date, "date" ).toString() ); }
252
253    /**
254     *  <p>{@summary Adds the provided {@code int} value as a {@code CDATA}
255     *  element to this XML element.}</p>
256     *  <p>To convert the number to text, the method
257     *  {@link Integer#toString(int)}
258     *  is used.</p>
259     *
260     *  @param  number  The number.
261     *  @return This instance.
262     *  @throws IllegalOperationException    No text allowed for this element.
263     */
264    @SuppressWarnings( "UnusedReturnValue" )
265    public default XMLElement addCDATA( final int number ) throws IllegalOperationException { return addCDATA( Integer.toString( number ) ); }
266
267    /**
268     *  <p>{@summary Adds the provided
269     *  {@link LocalDate}
270     *  instance as a {@code CDATA} element to this XML element.}</p>
271     *  <p>To convert the date to text, the method
272     *  {@link LocalDate#toString()}
273     *  is used.</p>
274     *
275     *  @param  date    The date.
276     *  @return This instance.
277     *  @throws IllegalOperationException    No text allowed for this element.
278     */
279    @SuppressWarnings( "UnusedReturnValue" )
280    public default XMLElement addCDATA( final LocalDate date ) throws IllegalOperationException { return addCDATA( requireNonNullArgument( date, "date" ).toString() ); }
281
282    /**
283     *  <p>{@summary Adds the provided
284     *  {@link LocalDateTime}
285     *  instance as a {@code CDATA} element to this XML element.}</p>
286     *  <p>To convert the date to text, the method
287     *  {@link LocalDateTime#toString()}
288     *  is used.</p>
289     *
290     *  @param  date    The date.
291     *  @return This instance.
292     *  @throws IllegalOperationException    No text allowed for this element.
293     */
294    @SuppressWarnings( "UnusedReturnValue" )
295    public default XMLElement addCDATA( final LocalDateTime date ) throws IllegalOperationException { return addCDATA( requireNonNullArgument( date, "date" ).toString() ); }
296
297    /**
298     *  <p>{@summary Adds the provided {@code long} value as a {@code CDATA}
299     *  element to this XML element.}</p>
300     *  <p>To convert the number to text, the method
301     *  {@link Long#toString(long)}
302     *  is used.</p>
303     *
304     *  @param  number  The number.
305     *  @return This instance.
306     *  @throws IllegalOperationException    No text allowed for this element.
307     */
308    @SuppressWarnings( "UnusedReturnValue" )
309    public default XMLElement addCDATA( final long number ) throws IllegalOperationException { return addCDATA( Long.toString( number ) ); }
310
311    /**
312     *  <p>{@summary Adds the provided
313     *  {@link Number}
314     *  instance as a {@code CDATA} element to this XML element.}</p>
315     *  <p>To convert the number to text, the method
316     *  {@link Number#toString()}
317     *  is used.</p>
318     *
319     *  @param  number  The number.
320     *  @return This instance.
321     *  @throws IllegalOperationException    No text allowed for this element.
322     */
323    @SuppressWarnings( "UnusedReturnValue" )
324    public default XMLElement addCDATA( final Number number ) throws IllegalOperationException { return addCDATA( requireNonNullArgument( number, "number" ).toString() ); }
325
326    /**
327     *  <p>{@summary Adds the provided
328     *  {@link ZonedDateTime}
329     *  instance as a {@code CDATA} element to this XML element.}</p>
330     *  <p>To convert the date to text, the method
331     *  {@link ZonedDateTime#toString()}
332     *  is used.</p>
333     *
334     *  @param  date    The date.
335     *  @return This instance.
336     *  @throws IllegalOperationException    No text allowed for this element.
337     */
338    @SuppressWarnings( "UnusedReturnValue" )
339    public default XMLElement addCDATA( final ZonedDateTime date ) throws IllegalOperationException { return addCDATA( requireNonNullArgument( date, "date" ).toString() ); }
340
341    /**
342     *  Adds a child to this element.
343     *
344     *  @param  <E> The implementation type for the {@code children}.
345     *  @param  child   The child to add.
346     *  @return This instance.
347     *  @throws IllegalArgumentException    The given child is not valid for
348     *      this element.
349     *  @throws IllegalOperationException   No children are allowed for this
350     *      element.
351     *  @throws IllegalStateException   The child has already a parent that is
352     *      not this element.
353     */
354    @SuppressWarnings( "UnusedReturnValue" )
355    public default <E extends XMLElement> XMLElement addChild( final E child ) throws IllegalArgumentException, IllegalOperationException, IllegalStateException
356    {
357        throw new IllegalOperationException( "No children allowed for element '%1$s'".formatted( getElementName() ) );
358    }   //  addChild()
359
360    /**
361     *  Adds a comment.
362     *
363     *  @param  comment The comment text.
364     *  @return This instance.
365     *  @throws IllegalOperationException    No comment allowed for this element.
366     */
367    @SuppressWarnings( "UnusedReturnValue" )
368    public default XMLElement addComment( final CharSequence comment ) throws IllegalOperationException
369    {
370        throw new IllegalOperationException( "No comment allowed for element '%1$s'".formatted( getElementName() ) );
371    }   //  addComment()
372
373    /**
374     *  <p>{@summary Adds predefined markup.}</p>
375     *  <p>The given markup will not be validated, it just may not be
376     *  {@code null}. So the caller is responsible that it will be proper
377     *  markup.</p>
378     *  <p>As the markup may be formatted differently (or not formatted at
379     *  all), the pretty printed output may be distorted when this is used.</p>
380     *
381     *  @param  markup  The predefined markup.
382     *  @return This instance.
383     *  @throws IllegalOperationException    No children are allowed for this
384     *      element.
385     */
386    @SuppressWarnings( "UnusedReturnValue" )
387    public default XMLElement addPredefinedMarkup( final CharSequence markup ) throws IllegalOperationException
388    {
389        throw new IllegalOperationException( "No children allowed for element '%1$s'".formatted( getElementName() ) );
390    }   //  addPredefinedMarkup()
391
392    /**
393     *  <p>{@summary Adds the provided {@code boolean} value as text to this
394     *  element.}</p>
395     *  <p>To convert the boolean to text, the method
396     *  {@link Boolean#toString(boolean)}
397     *  is used.</p>
398     *
399     *  @param  flag    The value.
400     *  @return This instance.
401     *  @throws IllegalOperationException    No text allowed for this element.
402     */
403    @SuppressWarnings( "UnusedReturnValue" )
404    public default XMLElement addText( final boolean flag ) throws IllegalOperationException { return addText( Boolean.toString( flag ) ); }
405
406    /**
407     *  <p>{@summary Adds the provided
408     *  {@link Boolean}
409     *  instance as text to this element.}</p>
410     *  <p>To convert the boolean to text, the method
411     *  {@link Boolean#toString()}
412     *  is used.</p>
413     *
414     *  @param  flag    The value.
415     *  @return This instance.
416     *  @throws IllegalOperationException    No text allowed for this element.
417     */
418    @SuppressWarnings( "UnusedReturnValue" )
419    public default XMLElement addText( final Boolean flag ) throws IllegalOperationException { return addText( requireNonNullArgument( flag, "flag" ).toString() ); }
420
421    /**
422     *  <p>{@summary Adds the provided {@code char} value as text to this
423     *  element.}</p>
424     *  <p>To convert the character to text, the method
425     *  {@link Character#toString(char)}
426     *  is used.</p>
427     *
428     *  @param  c   The character.
429     *  @return This instance.
430     *  @throws IllegalOperationException    No text allowed for this element.
431     */
432    @SuppressWarnings( "UnusedReturnValue" )
433    public default XMLElement addText( final char c ) throws IllegalOperationException { return addText( Character.toString( c ) ); }
434
435    /**
436     *  <p>{@summary Adds the provided
437     *  {@link Character}
438     *  instance as text to this element.}</p>
439     *  <p>To convert the character to text, the method
440     *  {@link Character#toString()}
441     *  is used.</p>
442     *
443     *  @param  c   The character.
444     *  @return This instance.
445     *  @throws IllegalOperationException    No text allowed for this element.
446     */
447    @SuppressWarnings( "UnusedReturnValue" )
448    public default XMLElement addText( final Character c ) throws IllegalOperationException { return addText( requireNonNullArgument( c, "c" ).toString() ); }
449
450    /**
451     *  Adds text to this element. Special characters will be escaped
452     *  according to the rules for the respective SGML flavour.
453     *
454     *  @param  text    The text.
455     *  @return This instance.
456     *  @throws IllegalOperationException    No text allowed for this element.
457     */
458    public default XMLElement addText( final CharSequence text ) throws IllegalOperationException
459    {
460        throw new IllegalOperationException( "No text allowed for element '%1$s'".formatted( getElementName() ) );
461    }   //  addText()
462
463    /**
464     *  <p>{@summary Adds the provided {@code double} value as text to this
465     *  element.}</p>
466     *  <p>To convert the number to text, the method
467     *  {@link Double#toString(double)}
468     *  is used.</p>
469     *
470     *  @param  number  The number.
471     *  @return This instance.
472     *  @throws IllegalOperationException    No text allowed for this element.
473     */
474    @SuppressWarnings( "UnusedReturnValue" )
475    public default XMLElement addText( final double number ) throws IllegalOperationException { return addText( Double.toString( number ) ); }
476
477    /**
478     *  <p>{@summary Adds the provided {@code enum} instance as text element to this
479     *  element.}</p>
480     *  <p>To convert the value to text, the method
481     *  {@link Enum#name()}
482     *  is used.</p>
483     *
484     *  @param  <E> The type of the {@code enum} value.
485     *  @param  value   The value.
486     *  @return This instance.
487     *  @throws IllegalOperationException    No text allowed for this element.
488     */
489    @SuppressWarnings( "UnusedReturnValue" )
490    public default <E extends Enum<E>> XMLElement addText( final E value ) throws IllegalOperationException { return addText( requireNonNullArgument( value, "value" ).name() ); }
491
492    /**
493     *  <p>{@summary Adds the provided
494     *  {@link Instant}
495     *  instance as text to this element.}</p>
496     *  <p>To convert the date to text, the method
497     *  {@link Instant#toString()}
498     *  is used.</p>
499     *
500     *  @param  date    The date.
501     *  @return This instance.
502     *  @throws IllegalOperationException    No text allowed for this element.
503     */
504    @SuppressWarnings( "UnusedReturnValue" )
505    public default XMLElement addText( final Instant date ) throws IllegalOperationException { return addText( requireNonNullArgument( date, "date" ).toString() ); }
506
507    /**
508     *  <p>{@summary Adds the provided {@code int} value as text to this
509     *  element.}</p>
510     *  <p>To convert the number to text, the method
511     *  {@link Integer#toString(int)}
512     *  is used.</p>
513     *
514     *  @param  number  The number.
515     *  @return This instance.
516     *  @throws IllegalOperationException    No text allowed for this element.
517     */
518    @SuppressWarnings( "UnusedReturnValue" )
519    public default XMLElement addText( final int number ) throws IllegalOperationException { return addText( Integer.toString( number ) ); }
520
521    /**
522     *  <p>{@summary Adds the provided
523     *  {@link LocalDate}
524     *  instance as text to this element.}</p>
525     *  <p>To convert the date to text, the method
526     *  {@link LocalDate#toString()}
527     *  is used.</p>
528     *
529     *  @param  date    The date.
530     *  @return This instance.
531     *  @throws IllegalOperationException    No text allowed for this element.
532     */
533    @SuppressWarnings( "UnusedReturnValue" )
534    public default XMLElement addText( final LocalDate date ) throws IllegalOperationException { return addText( requireNonNullArgument( date, "date" ).toString() ); }
535
536    /**
537     *  <p>{@summary Adds the provided
538     *  {@link LocalDateTime}
539     *  instance as text to this element.}</p>
540     *  <p>To convert the date to text, the method
541     *  {@link LocalDateTime#toString()}
542     *  is used.</p>
543     *
544     *  @param  date    The date.
545     *  @return This instance.
546     *  @throws IllegalOperationException    No text allowed for this element.
547     */
548    @SuppressWarnings( "UnusedReturnValue" )
549    public default XMLElement addText( final LocalDateTime date ) throws IllegalOperationException { return addText( requireNonNullArgument( date, "date" ).toString() ); }
550
551    /**
552     *  <p>{@summary Adds the provided {@code long} value as text to this
553     *  element.}</p>
554     *  <p>To convert the number to text, the method
555     *  {@link Long#toString(long)}
556     *  is used.</p>
557     *
558     *  @param  number  The number.
559     *  @return This instance.
560     *  @throws IllegalOperationException    No text allowed for this element.
561     */
562    @SuppressWarnings( "UnusedReturnValue" )
563    public default XMLElement addText( final long number ) throws IllegalOperationException { return addText( Long.toString( number ) ); }
564
565    /**
566     *  <p>{@summary Adds the provided
567     *  {@link Number}
568     *  instance as text to this element.}</p>
569     *  <p>To convert the number to text, the method
570     *  {@link Number#toString()}
571     *  is used.</p>
572     *
573     *  @param  number  The number.
574     *  @return This instance.
575     *  @throws IllegalOperationException    No text allowed for this element.
576     */
577    @SuppressWarnings( "UnusedReturnValue" )
578    public default XMLElement addText( final Number number ) throws IllegalOperationException { return addText( requireNonNullArgument( number, "number" ).toString() ); }
579
580    /**
581     *  <p>{@summary Adds the provided
582     *  {@link ZonedDateTime}
583     *  instance as text to this element.}</p>
584     *  <p>To convert the date to text, the method
585     *  {@link ZonedDateTime#toString()}
586     *  is used.</p>
587     *
588     *  @param  date    The date.
589     *  @return This instance.
590     *  @throws IllegalOperationException    No text allowed for this element.
591     */
592    @SuppressWarnings( "UnusedReturnValue" )
593    public default XMLElement addText( final ZonedDateTime date ) throws IllegalOperationException { return addText( requireNonNullArgument( date, "date" ).toString() ); }
594
595    /**
596     *  Returns the flags for this element.
597     *
598     *  @return The flags.
599     *
600     *  @see Flags
601     */
602    public Set<Flags> getFlags();
603
604    /**
605     *  <p>{@summary Sets the attribute with the given name.}</p>
606     *  <p>The method uses
607     *  {@link Boolean#toString(boolean)}
608     *  to convert the provided flag to a {@code String}.</p>
609     *
610     *  @param  name    The name of the attribute; the name is case-sensitive.
611     *  @param  flag    The attribute's value.
612     *  @return This instance.
613     *  @throws IllegalArgumentException    An attribute with the given name is
614     *      not valid for the element
615     *  @throws IllegalOperationException   No attributes are allowed for this
616     *      element.
617     */
618    public default XMLElement setAttribute( final String name, final boolean flag ) throws IllegalArgumentException, IllegalOperationException
619    {
620        return setAttribute( name, Boolean.toString( flag ) );
621    }   //  setAttribute()
622
623    /**
624     *  <p>{@summary Sets the attribute with the given name.}</p>
625     *  <p>The method uses
626     *  {@link Boolean#toString()}
627     *  to convert the provided flag to a {@code String}.</p>
628     *
629     *  @param  name    The name of the attribute; the name is case-sensitive.
630     *  @param  flag    The attribute's value; if {@code null} the
631     *      attribute will be removed.
632     *  @return This instance.
633     *  @throws IllegalArgumentException    An attribute with the given name is
634     *      not valid for the element
635     *  @throws IllegalOperationException   No attributes are allowed for this
636     *      element.
637     */
638    public default XMLElement setAttribute( final String name, final Boolean flag ) throws IllegalArgumentException, IllegalOperationException
639    {
640        return setAttribute( name, nonNull( flag ) ? Boolean.toString( flag ) : null );
641    }   //  setAttribute()
642
643    /**
644     *  Sets the attribute with the given name.
645     *
646     *  @param  name    The name of the attribute; the name is case-sensitive.
647     *  @param  value   The attribute's value; if {@code null} the
648     *      attribute will be removed.
649     *  @return This instance.
650     *  @throws IllegalArgumentException    An attribute with the given name is
651     *      not valid for the element
652     *  @throws IllegalOperationException   No attributes are allowed for this
653     *      element.
654     */
655    public default XMLElement setAttribute( final String name, final CharSequence value ) throws IllegalArgumentException, IllegalOperationException
656    {
657        return setAttribute( requireNotEmptyArgument( name, "name" ), value, NO_APPEND );
658    }   //  setAttribute()
659
660    /**
661     *  Sets the attribute with the given name.
662     *
663     *  @param  name    The name of the attribute; the name is case-sensitive.
664     *  @param  value   The attribute's value; if {@code null} the
665     *      attribute will be removed.
666     *  @param  append  If not
667     *      {@linkplain Optional#empty() empty}
668     *      ({@link #NO_APPEND}),
669     *      the new value will be appended to an already existing one, and the
670     *      provided char sequence is used as the separator.
671     *  @return This instance.
672     *  @throws IllegalArgumentException    An attribute with the given name is
673     *      not valid for the element
674     *  @throws IllegalOperationException   No attributes are allowed for this
675     *      element.
676     */
677    @SuppressWarnings( "OptionalUsedAsFieldOrParameterType" )
678    public default XMLElement setAttribute( final String name, final CharSequence value, final Optional<? extends CharSequence> append ) throws IllegalArgumentException, IllegalOperationException
679    {
680        throw new IllegalArgumentException( "No attributes allowed for element '%1$s'".formatted( getElementName() ) );
681    }   //  setAttribute()
682
683    /**
684     *  <p>{@summary Sets the attribute with the given name.}</p>
685     *  <p>The method uses
686     *  {@link Double#toString(double)}
687     *  to convert the provided number to a {@code String}.</p>
688     *
689     *  @param  name    The name of the attribute; the name is case-sensitive.
690     *  @param  number   The attribute's value.
691     *  @return This instance.
692     *  @throws IllegalArgumentException    An attribute with the given name is
693     *      not valid for the element
694     *  @throws IllegalOperationException   No attributes are allowed for this
695     *      element.
696     */
697    public default XMLElement setAttribute( final String name, final double number ) throws IllegalArgumentException, IllegalOperationException
698    {
699        return setAttribute( name, Double.toString( number ) );
700    }   //  setAttribute()
701
702    /**
703     *  <p>{@summary Sets the attribute with the given name.}</p>
704     *  <p>The method uses
705     *  {@link Enum#name()}
706     *  to convert the provided value to a {@code String}.</p>
707     *
708     *  @param  <E> The concrete enum type of {@code value}.
709     *  @param  name    The name of the attribute; the name is case-sensitive.
710     *  @param  enumValue   The attribute's value; if {@code null} the
711     *      attribute will be removed.
712     *  @return This instance.
713     *  @throws IllegalArgumentException    An attribute with the given name is
714     *      not valid for the element
715     *  @throws IllegalOperationException   No attributes are allowed for this
716     *      element.
717     */
718    public default <E extends Enum<E>> XMLElement setAttribute( final String name, final E enumValue ) throws IllegalArgumentException, IllegalOperationException
719    {
720        return setAttribute( name, nonNull( enumValue ) ? enumValue.name() : null );
721    }   //  setAttribute()
722
723    /**
724     *  <p>{@summary Sets the attribute with the given name.}</p>
725     *  <p>The method uses
726     *  {@link Instant#toString()}
727     *  to convert the provided number to a {@code String}.</p>
728     *
729     *  @param  name    The name of the attribute; the name is case-sensitive.
730     *  @param  date    The attribute's value; if {@code null} the
731     *      attribute will be removed.
732     *  @return This instance.
733     *  @throws IllegalArgumentException    An attribute with the given name is
734     *      not valid for the element
735     *  @throws IllegalOperationException   No attributes are allowed for this
736     *      element.
737     */
738    public default XMLElement setAttribute( final String name, final Instant date ) throws IllegalArgumentException, IllegalOperationException
739    {
740        return setAttribute( name, nonNull( date ) ? date.toString() : null );
741    }   //  setAttribute()
742
743    /**
744     *  <p>{@summary Sets the attribute with the given name.}</p>
745     *  <p>The method uses
746     *  {@link Integer#toString(int)}
747     *  to convert the provided number to a {@code String}.</p>
748     *
749     *  @param  name    The name of the attribute; the name is case-sensitive.
750     *  @param  number  The attribute's value.
751     *  @return This instance.
752     *  @throws IllegalArgumentException    An attribute with the given name is
753     *      not valid for the element
754     *  @throws IllegalOperationException   No attributes are allowed for this
755     *      element.
756     */
757    public default XMLElement setAttribute( final String name, final int number ) throws IllegalArgumentException, IllegalOperationException
758    {
759        return setAttribute( name, Integer.toString( number ) );
760    }   //  setAttribute()
761
762    /**
763     *  <p>{@summary Sets the attribute with the given name.}</p>
764     *  <p>The method uses
765     *  {@link LocalDate#toString()}
766     *  to convert the provided number to a {@code String}.</p>
767     *
768     *  @param  name    The name of the attribute; the name is case-sensitive.
769     *  @param  date    The attribute's value; if {@code null} the
770     *      attribute will be removed.
771     *  @return This instance.
772     *  @throws IllegalArgumentException    An attribute with the given name is
773     *      not valid for the element
774     *  @throws IllegalOperationException   No attributes are allowed for this
775     *      element.
776     */
777    public default XMLElement setAttribute( final String name, final LocalDate date ) throws IllegalArgumentException, IllegalOperationException
778    {
779        return setAttribute( name, nonNull( date ) ? date.toString() : null );
780    }   //  setAttribute()
781
782    /**
783     *  <p>{@summary Sets the attribute with the given name.}</p>
784     *  <p>The method uses
785     *  {@link LocalDateTime#toString()}
786     *  to convert the provided number to a {@code String}.</p>
787     *
788     *  @param  name    The name of the attribute; the name is case-sensitive.
789     *  @param  date    The attribute's value; if {@code null} the
790     *      attribute will be removed.
791     *  @return This instance.
792     *  @throws IllegalArgumentException    An attribute with the given name is
793     *      not valid for the element
794     *  @throws IllegalOperationException   No attributes are allowed for this
795     *      element.
796     */
797    public default XMLElement setAttribute( final String name, final LocalDateTime date ) throws IllegalArgumentException, IllegalOperationException
798    {
799        return setAttribute( name, nonNull( date ) ? date.toString() : null );
800    }   //  setAttribute()
801
802    /**
803     *  <p>{@summary Sets the attribute with the given name.}</p>
804     *  <p>The method uses
805     *  {@link Long#toString(long)}
806     *  to convert the provided number to a {@code String}.</p>
807     *
808     *  @param  name    The name of the attribute; the name is case-sensitive.
809     *  @param  number   The attribute's value.
810     *  @return This instance.
811     *  @throws IllegalArgumentException    An attribute with the given name is
812     *      not valid for the element
813     *  @throws IllegalOperationException   No attributes are allowed for this
814     *      element.
815     */
816    public default XMLElement setAttribute( final String name, final long number ) throws IllegalArgumentException, IllegalOperationException
817    {
818        return setAttribute( name, Long.toString( number ) );
819    }   //  setAttribute()
820
821    /**
822     *  <p>{@summary Sets the attribute with the given name.}</p>
823     *  <p>The method uses
824     *  {@link Number#toString()}
825     *  to convert the provided number to a {@code String}.</p>
826     *
827     *  @param  name    The name of the attribute; the name is case-sensitive.
828     *  @param  number   The attribute's value; if {@code null} the
829     *      attribute will be removed.
830     *  @return This instance.
831     *  @throws IllegalArgumentException    An attribute with the given name is
832     *      not valid for the element
833     *  @throws IllegalOperationException   No attributes are allowed for this
834     *      element.
835     */
836    public default XMLElement setAttribute( final String name, final Number number ) throws IllegalArgumentException, IllegalOperationException
837    {
838        return setAttribute( name, nonNull( number ) ? number.toString() : null );
839    }   //  setAttribute()
840
841    /**
842     *  <p>{@summary Sets the attribute with the given name.}</p>
843     *  <p>The method uses
844     *  {@link ZonedDateTime#toString()}
845     *  to convert the provided number to a {@code String}.</p>
846     *
847     *  @param  name    The name of the attribute; the name is case-sensitive.
848     *  @param  date    The attribute's value; if {@code null} the
849     *      attribute will be removed.
850     *  @return This instance.
851     *  @throws IllegalArgumentException    An attribute with the given name is
852     *      not valid for the element
853     *  @throws IllegalOperationException   No attributes are allowed for this
854     *      element.
855     */
856    public default XMLElement setAttribute( final String name, final ZonedDateTime date ) throws IllegalArgumentException, IllegalOperationException
857    {
858        return setAttribute( name, nonNull( date ) ? date.toString() : null );
859    }   //  setAttribute()
860
861    /**
862     *  <p>{@summary Sets the attribute with the given name if the provided
863     *  value is not empty.}</p>
864     *  <p>The method uses
865     *  {@link org.tquadrat.foundation.util.StringUtils#isNotEmpty(CharSequence)}
866     *  to test if the given value is empty.</p>
867     *
868     *  @param  name    The name of the attribute.
869     *  @param  value   The value for the attribute; can be {@code null}.
870     *  @return This instance.
871     *  @throws IllegalArgumentException    An attribute with the given name is
872     *      not valid for the element
873     *  @throws IllegalOperationException   No attributes are allowed for this
874     *      element.
875     */
876    @SuppressWarnings( "UnusedReturnValue" )
877    public default XMLElement setAttributeIfNotEmpty( final String name, final CharSequence value ) throws IllegalArgumentException, IllegalOperationException
878    {
879        if( isNotEmpty( value ) ) setAttribute( name, value );
880
881        //---* Done *----------------------------------------------------------
882        return this;
883    }   //  setAttributeIfNotEmpty()
884
885    /**
886     *  Sets the attribute with the given name if the provided value is not
887     *  empty.
888     *
889     *  @param  name    The name of the attribute.
890     *  @param  optional   The value for the attribute.
891     *  @return This instance.
892     *  @throws IllegalArgumentException    An attribute with the given name is
893     *      not valid for the element
894     *  @throws IllegalOperationException   No attributes are allowed for this
895     *      element.
896     */
897    @SuppressWarnings( {"OptionalUsedAsFieldOrParameterType", "UnusedReturnValue"} )
898    public default XMLElement setAttributeIfNotEmpty( final String name, final Optional<? extends CharSequence> optional ) throws IllegalArgumentException, IllegalOperationException
899    {
900        requireNonNullArgument( optional, "optional" ).ifPresent( value -> setAttribute( name, value ) );
901
902        //---* Done *----------------------------------------------------------
903        return this;
904    }   //  setAttributeIfNotEmpty()
905
906    /**
907     *  <p>{@summary Sets the id for the element.}</p>
908     *  <p>The value will be validated using the method that is provided by a
909     *  call to
910     *  {@link XMLBuilderUtils#getNMTokenValidator()}.</p>
911     *
912     *  @param  id  The id.
913     *  @return This instance.
914     *  @throws IllegalArgumentException    An attribute with the given name is
915     *      not valid for the element or the value is not a valid NMToken.
916     *  @throws IllegalOperationException   No attributes are allowed for this
917     *      element.
918     *
919     *  @see org.tquadrat.foundation.lang.CommonConstants#XMLATTRIBUTE_Id
920     */
921    public default XMLElement setId( final String id ) throws IllegalArgumentException, IllegalOperationException
922    {
923        if( !getNMTokenValidator().test( requireNotEmptyArgument( id, "id" ) ) ) throw new IllegalArgumentException( "Invalid id: %s".formatted( id ) );
924        final var retValue = setAttribute( XMLATTRIBUTE_Id, id );
925
926        //---* Done *----------------------------------------------------------
927        return retValue;
928    }   //  setId()
929
930    /**
931     *  Sets the given namespace.
932     *
933     *  @param  identifier  The namespace identifier.
934     *  @return This instance.
935     *  @throws IllegalOperationException    Namespaces are not allowed for this
936     *      element.
937     *  @throws URISyntaxException  The provided URI String is invalid.
938     */
939    public default XMLElement setNamespace( final String identifier ) throws IllegalOperationException, URISyntaxException
940    {
941        final var namespace = new Namespace( identifier );
942        final var retValue = setNamespace( namespace );
943
944        //---* Done *----------------------------------------------------------
945        return retValue;
946    }   //  setNamespace()
947
948    /**
949     *  Sets the given namespace.
950     *
951     *  @param  identifier  The namespace identifier.
952     *  @return This instance.
953     *  @throws IllegalOperationException    Namespaces are not allowed for this
954     *      element.
955     */
956    public default XMLElement setNamespace( final URI identifier ) throws IllegalOperationException
957    {
958        final var namespace = new Namespace( identifier );
959        final var retValue = setNamespace( namespace );
960
961        //---* Done *----------------------------------------------------------
962        return retValue;
963    }   //  setNamespace()
964
965    /**
966     *  <p>{@summary Sets the given namespace.}</p>
967     *  <p>The given prefix is validated using the method that is
968     *  provided by
969     *  {@link org.tquadrat.foundation.xml.builder.XMLBuilderUtils#getPrefixValidator()}.</p>
970     *
971     *  @param  prefix  The namespace prefix.
972     *  @param  identifier  The namespace identifier.
973     *  @return This instance.
974     *  @throws IllegalOperationException    Namespaces are not allowed for this
975     *      element.
976     *  @throws URISyntaxException  The provided URI String is invalid.
977     */
978    public default XMLElement setNamespace( final String prefix, final String identifier ) throws IllegalOperationException, URISyntaxException
979    {
980        final var namespace = new Namespace( prefix, identifier );
981        final var retValue = setNamespace( namespace );
982
983        //---* Done *----------------------------------------------------------
984        return retValue;
985    }   //  setNamespace()
986
987    /**
988     *  <p>{@summary Sets the given namespace.}</p>
989     *  <p>The given prefix is validated using the method that is
990     *  provided by
991     *  {@link org.tquadrat.foundation.xml.builder.XMLBuilderUtils#getPrefixValidator()}.</p>
992     *
993     *  @param  prefix  The namespace prefix.
994     *  @param  identifier  The namespace identifier.
995     *  @return This instance.
996     *  @throws IllegalOperationException    Namespaces are not allowed for this
997     *      element.
998     */
999    public default XMLElement setNamespace( final String prefix, final URI identifier ) throws IllegalOperationException
1000    {
1001        final var namespace = new Namespace( prefix, identifier );
1002        final var retValue = setNamespace( namespace );
1003
1004        //---* Done *----------------------------------------------------------
1005        return retValue;
1006    }   //  setNamespace()
1007
1008    /**
1009     *  Sets the given namespace.
1010     *
1011     *  @param  namespace   The namespace.
1012     *  @return This instance.
1013     *  @throws IllegalOperationException    Namespaces are not allowed for this
1014     *      element.
1015     */
1016    @SuppressWarnings( "UseOfConcreteClass" )
1017    public default XMLElement setNamespace( final Namespace namespace ) throws IllegalOperationException
1018    {
1019        throw new IllegalOperationException( "No namespace allowed for element '%1$s'".formatted( getElementName() ) );
1020    }   //  setNamespace()
1021
1022    /**
1023     *  {@inheritDoc}
1024     */
1025    @Override
1026    public <E extends Element> void setParent( final E parent );
1027}
1028//  interface XMLElement
1029
1030/*
1031 *  End of File
1032 */