View Javadoc
1   /*
2    * Copyright 2020-2022  the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.bremersee.xml;
18  
19  import java.util.Collections;
20  import java.util.LinkedHashSet;
21  import java.util.Optional;
22  import java.util.Set;
23  import java.util.stream.Collector;
24  import java.util.stream.Collectors;
25  import org.springframework.util.ClassUtils;
26  import org.springframework.util.StringUtils;
27  
28  /**
29   * The jaxb context builder details.
30   *
31   * @author Christian Bremer
32   */
33  public interface JaxbContextDetails {
34  
35    /**
36     * Context data collector collector.
37     *
38     * @return the collector
39     */
40    static Collector<JaxbContextData, ?, JaxbContextDetailsImpl> contextDataCollector() {
41      return Collector.of(JaxbContextDetailsImpl::new, JaxbContextDetailsImpl::add, (left, right) -> {
42        if (left.size() < right.size()) {
43          right.addAll(left);
44          return right;
45        } else {
46          left.addAll(right);
47          return left;
48        }
49      });
50    }
51  
52    /**
53     * Returns empty jaxb context details.
54     *
55     * @return the empty jaxb context details
56     */
57    static JaxbContextDetails empty() {
58      return new JaxbContextDetailsImpl();
59    }
60  
61    /**
62     * Is empty boolean.
63     *
64     * @return the boolean
65     */
66    boolean isEmpty();
67  
68    /**
69     * Get classes.
70     *
71     * @param classLoaders the class loaders
72     * @return the classes
73     */
74    Class<?>[] getClasses(ClassLoader... classLoaders);
75  
76    /**
77     * Gets schema location.
78     *
79     * @return the schema location
80     */
81    String getSchemaLocation();
82  
83    /**
84     * Gets name spaces with schema locations.
85     *
86     * @return the name spaces with schema locations
87     */
88    default Set<String> getNameSpacesWithSchemaLocations() {
89      return Optional.of(schemaLocationToArray(getSchemaLocation()))
90          .map(parts -> {
91            Set<String> locations = new LinkedHashSet<>();
92            for (int i = 1; i < parts.length; i = i + 2) {
93              locations.add(parts[i - 1] + " " + parts[i]);
94            }
95            return locations;
96          })
97          .orElseGet(Collections::emptySet);
98    }
99  
100   /**
101    * Gets the set of name spaces where the schema location is present.
102    *
103    * @return the name spaces
104    */
105   default Set<String> getNameSpaces() {
106     return Optional.of(schemaLocationToArray(getSchemaLocation()))
107         .map(parts -> {
108           Set<String> nameSpaces = new LinkedHashSet<>();
109           for (int i = 0; i < parts.length; i = i + 2) {
110             nameSpaces.add(parts[i]);
111           }
112           return nameSpaces;
113         })
114         .orElseGet(Collections::emptySet);
115   }
116 
117   /**
118    * Gets the set of schema locations, normally as URL.
119    * <pre>
120    * http://example.org/model.xsd, http://example.org/another-model.xsd
121    * </pre>
122    *
123    * @return the schema locations
124    */
125   default Set<String> getSchemaLocations() {
126     return Optional.of(schemaLocationToArray(getSchemaLocation()))
127         .map(parts -> {
128           Set<String> locations = new LinkedHashSet<>();
129           for (int i = 1; i < parts.length; i = i + 2) {
130             locations.add(parts[i]);
131           }
132           return locations;
133         })
134         .orElse(Collections.emptySet());
135   }
136 
137   private static String[] schemaLocationToArray(String schemaLocation) {
138     return Optional.ofNullable(schemaLocation)
139         .map(sl -> sl.replaceAll("^\\s*|\\s*$", ""))
140         .map(sl -> sl.replaceAll("[\\s]{2,}", " "))
141         .map(sl -> StringUtils.delimitedListToStringArray(sl, " "))
142         .orElseGet(() -> new String[0]);
143   }
144 
145   /**
146    * The jaxb context details implementation.
147    */
148   class JaxbContextDetailsImpl
149       extends LinkedHashSet<JaxbContextData>
150       implements JaxbContextDetails {
151 
152     /**
153      * Instantiates a jaxb contexts details.
154      */
155     private JaxbContextDetailsImpl() {
156       super();
157     }
158 
159     @Override
160     public String getSchemaLocation() {
161       return this.stream()
162           .flatMap(JaxbContextData::getNameSpacesWithSchemaLocations)
163           .distinct()
164           .sorted()
165           .map(SchemaLocation::toString)
166           .collect(Collectors.joining(" "));
167     }
168 
169     @Override
170     public Class<?>[] getClasses(ClassLoader... classLoaders) {
171       return ClassUtils
172           .toClassArray(this
173               .stream()
174               .flatMap(data -> data.getJaxbClasses(classLoaders))
175               .collect(Collectors.toSet()));
176     }
177 
178   }
179 
180 }