XMLProvider.java
001 /*
002  * Java GPX Library (jpx-3.1.0).
003  * Copyright (c) 2016-2023 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * 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  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019  *    Antoine Vianey (https://github.com/avianey)
020  */
021 package io.jenetics.jpx;
022 
023 import java.util.Iterator;
024 import java.util.ServiceLoader;
025 
026 import javax.xml.parsers.DocumentBuilderFactory;
027 import javax.xml.stream.XMLInputFactory;
028 import javax.xml.stream.XMLOutputFactory;
029 
030 /**
031  * A {@link ServiceLoader} for managing XML factories used by the library.
032  * Custom implementation should be referenced in a
033  * {@code META-INF/services/io.jenetics.jpx.XMLProvider} file.
034  *
035  @see ServiceLoader
036  *
037  @version 1.7
038  @since 1.7
039  */
040 public abstract class XMLProvider {
041 
042     private static final Object LOCK = new Object(){};
043 
044     private static volatile XMLProvider INSTANCE;
045 
046     protected XMLProvider() {
047     }
048 
049     /**
050      * Returns {@link XMLInputFactory} to be used for reading files.
051      *
052      @return the xml input factory
053      */
054     public XMLInputFactory xmlInputFactory() {
055         return XMLInputFactory.newInstance();
056     }
057 
058     /**
059      * Returns {@link XMLOutputFactory} to be used for writing files.
060      *
061      @return the xml output factory
062      */
063     public XMLOutputFactory xmlOutputFactory() {
064         return XMLOutputFactory.newInstance();
065     }
066 
067     /**
068      * Returns the {@link DocumentBuilderFactory} used for handling extensions documents
069      *
070      @return the document builder factory
071      */
072     public DocumentBuilderFactory documentBuilderFactory() {
073         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
074         factory.setIgnoringElementContentWhitespace(true);
075         factory.setNamespaceAware(true);
076         return factory;
077     }
078 
079     /**
080      * Return an instance of the current {@code XMLProvider}.
081      *
082      @return an instance of the current {@code XMLProvider}
083      */
084     public static XMLProvider provider() {
085         if (INSTANCE == null) {
086             synchronized (LOCK) {
087                 if (INSTANCE == null) {
088                     loadInstance();
089                 }
090             }
091         }
092         return INSTANCE;
093     }
094 
095     /**
096      * Clear current spi to allow hot reloading a new one...
097      */
098     protected static void clear() {
099         if (INSTANCE != null) {
100             synchronized (LOCK) {
101                 if (INSTANCE != null) {
102                     INSTANCE = null;
103                 }
104             }
105         }
106     }
107 
108     private static void loadInstance() {
109         final ServiceLoader<XMLProvider> loader =
110             ServiceLoader.load(XMLProvider.class);
111 
112         final Iterator<XMLProvider> providers = loader.iterator();
113         if (providers.hasNext()) {
114             INSTANCE = providers.next();
115         else {
116             INSTANCE = new DefaultXMLProvider();
117         }
118     }
119 
120 }
121 
122 /**
123  * Default implementation of the {@code XMLProvider} class. Doesn't need any
124  * additional implementation.
125  */
126 final class DefaultXMLProvider extends XMLProvider { }