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 lombok.AccessLevel;
26  import lombok.NoArgsConstructor;
27  import org.springframework.util.ClassUtils;
28  import org.springframework.util.StringUtils;
29  
30  /**
31   * The jaxb context builder details.
32   *
33   * @author Christian Bremer
34   */
35  public interface JaxbContextDetails {
36  
37    /**
38     * Context data collector collector.
39     *
40     * @return the collector
41     */
42    static Collector<JaxbContextData, ?, JaxbContextDetailsImpl> contextDataCollector() {
43      return Collector.of(JaxbContextDetailsImpl::new, JaxbContextDetailsImpl::add, (left, right) -> {
44        if (left.size() < right.size()) {
45          right.addAll(left);
46          return right;
47        } else {
48          left.addAll(right);
49          return left;
50        }
51      });
52    }
53  
54    /**
55     * Returns empty jaxb context details.
56     *
57     * @return the empty jaxb context details
58     */
59    static JaxbContextDetails empty() {
60      return new JaxbContextDetailsImpl();
61    }
62  
63    /**
64     * Is empty boolean.
65     *
66     * @return the boolean
67     */
68    boolean isEmpty();
69  
70    /**
71     * Get classes.
72     *
73     * @param classLoaders the class loaders
74     * @return the classes
75     */
76    Class<?>[] getClasses(ClassLoader... classLoaders);
77  
78    /**
79     * Gets schema location.
80     *
81     * @return the schema location
82     */
83    String getSchemaLocation();
84  
85    /**
86     * Gets name spaces with schema locations.
87     *
88     * @return the name spaces with schema locations
89     */
90    default Set<String> getNameSpacesWithSchemaLocations() {
91      return Optional.of(schemaLocationToArray(getSchemaLocation()))
92          .map(parts -> {
93            Set<String> locations = new LinkedHashSet<>();
94            for (int i = 1; i < parts.length; i = i + 2) {
95              locations.add(parts[i - 1] + " " + parts[i]);
96            }
97            return locations;
98          })
99          .orElseGet(Collections::emptySet);
100   }
101 
102   /**
103    * Gets the set of name spaces where the schema location is present.
104    *
105    * @return the name spaces
106    */
107   default Set<String> getNameSpaces() {
108     return Optional.of(schemaLocationToArray(getSchemaLocation()))
109         .map(parts -> {
110           Set<String> nameSpaces = new LinkedHashSet<>();
111           for (int i = 0; i < parts.length; i = i + 2) {
112             nameSpaces.add(parts[i]);
113           }
114           return nameSpaces;
115         })
116         .orElseGet(Collections::emptySet);
117   }
118 
119   /**
120    * Gets the set of schema locations, normally as URL.
121    * <pre>
122    * http://example.org/model.xsd, http://example.org/another-model.xsd
123    * </pre>
124    *
125    * @return the schema locations
126    */
127   default Set<String> getSchemaLocations() {
128     return Optional.of(schemaLocationToArray(getSchemaLocation()))
129         .map(parts -> {
130           Set<String> locations = new LinkedHashSet<>();
131           for (int i = 1; i < parts.length; i = i + 2) {
132             locations.add(parts[i]);
133           }
134           return locations;
135         })
136         .orElse(Collections.emptySet());
137   }
138 
139   private static String[] schemaLocationToArray(String schemaLocation) {
140     return Optional.ofNullable(schemaLocation)
141         .map(sl -> sl.replaceAll("^\\s*|\\s*$", ""))
142         .map(sl -> sl.replaceAll("[\\s]{2,}", " "))
143         .map(sl -> StringUtils.delimitedListToStringArray(sl, " "))
144         .orElseGet(() -> new String[0]);
145   }
146 
147   /**
148    * The jaxb context details implementation.
149    */
150   @SuppressWarnings("SameNameButDifferent")
151   @NoArgsConstructor(access = AccessLevel.PRIVATE)
152   class JaxbContextDetailsImpl
153       extends LinkedHashSet<JaxbContextData>
154       implements JaxbContextDetails {
155 
156     @Override
157     public String getSchemaLocation() {
158       return this.stream()
159           .flatMap(JaxbContextData::getNameSpacesWithSchemaLocations)
160           .distinct()
161           .sorted()
162           .map(SchemaLocation::toString)
163           .collect(Collectors.joining(" "));
164     }
165 
166     @Override
167     public Class<?>[] getClasses(ClassLoader... classLoaders) {
168       return ClassUtils
169           .toClassArray(this
170               .stream()
171               .flatMap(data -> data.getJaxbClasses(classLoaders))
172               .collect(Collectors.toSet()));
173     }
174 
175   }
176 
177 }