View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *  http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.any23.extractor.microdata;
19  
20  import org.apache.any23.rdf.RDFUtils;
21  import org.apache.commons.lang3.StringUtils;
22  import org.eclipse.rdf4j.common.net.ParsedIRI;
23  import org.eclipse.rdf4j.model.IRI;
24  
25  import java.net.MalformedURLException;
26  import java.net.URL;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Collection;
30  import java.util.Collections;
31  import java.util.HashMap;
32  import java.util.List;
33  import java.util.Locale;
34  import java.util.Map;
35  import java.util.regex.Pattern;
36  
37  /**
38   * This class describes a <b>Microdata <i>itemscope</i></b>.
39   *
40   * @author Michele Mostarda (mostarda@fbk.eu)
41   * @author Hans Brende (hansbrende@apache.org)
42   */
43  public class ItemScope extends Item {
44  
45      /**
46       * Map of properties and multi values.
47       */
48      private final Map<String, List<ItemProp>> properties;
49  
50      /**
51       * <i>itemscope</i> DOM identifier in container document.
52       */
53      private final String id;
54  
55      /**
56       * <i>itemscope</i> references.
57       */
58      private final String[] refs;
59  
60      /**
61       * <i>itemscope</i> type.
62       */
63      private final List<IRI> type;
64  
65      /**
66       * <i>itemscope</i> external identifier.
67       */
68      private final String itemId;
69  
70      /**
71       * Constructor.
72       *
73       * @param xpath
74       *            location of this <i>itemscope</i> within the container document.
75       * @param itemProps
76       *            list of properties bound to this <i>itemscope</i>.
77       * @param id
78       *            DOM identifier for this <i>itemscope</i>. Can be <code>null</code>.
79       * @param refs
80       *            list of item prop references connected to this <i>itemscope</i>. Can be <code>null</code>.
81       * @param type
82       *            <i>itemscope</i> type. Can be <code>null</code>.
83       * @param itemId
84       *            <i>itemscope</i> id. Can be <code>null</code>.
85       */
86      public ItemScope(String xpath, ItemProp[] itemProps, String id, String[] refs, String type, String itemId) {
87          this(xpath, itemProps, id, refs, stringToSingletonIRI(type), itemId);
88      }
89  
90      private static final Pattern looksLikeStartsWithHost = Pattern.compile("[^:/.]+(\\.[^:/.]+)+(:\\d+)?([/#?].*)?");
91  
92      static List<IRI> stringToSingletonIRI(String type) {
93          if (StringUtils.isNotBlank(type)) {
94              ParsedIRI iri = ParsedIRI.create(type.trim());
95              if (StringUtils.isBlank(iri.getScheme())) {
96                  String host = iri.getHost();
97                  if (StringUtils.isNotBlank(host)) {
98                      iri = new ParsedIRI("http", iri.getUserInfo(), host, iri.getPort(), iri.getPath(), iri.getQuery(),
99                              iri.getFragment());
100                 } else {
101                     String path = iri.getPath();
102                     if (path != null && looksLikeStartsWithHost.matcher(path).matches()) {
103                         iri = ParsedIRI.create("http://" + iri.toString());
104                     }
105                 }
106             }
107             return Collections.singletonList(RDFUtils.iri(iri.toString()));
108         } else {
109             return Collections.emptyList();
110         }
111     }
112 
113     ItemScope(String xpath, ItemProp[] itemProps, String id, String[] refs, List<IRI> types, String itemId) {
114         super(xpath);
115 
116         if (itemProps == null) {
117             throw new NullPointerException("itemProps list cannot be null.");
118         }
119 
120         this.type = types;
121         this.id = id;
122         this.refs = refs;
123         this.itemId = itemId;
124 
125         final Map<String, List<ItemProp>> tmpProperties = new HashMap<>();
126         for (ItemProp itemProp : itemProps) {
127             final String propName = itemProp.getName();
128             List<ItemProp> propList = tmpProperties.get(propName);
129             if (propList == null) {
130                 propList = new ArrayList<>();
131                 tmpProperties.put(propName, propList);
132             }
133             propList.add(itemProp);
134         }
135         final Map<String, List<ItemProp>> properties = new HashMap<>();
136         for (Map.Entry<String, List<ItemProp>> propertiesEntry : tmpProperties.entrySet()) {
137             properties.put(propertiesEntry.getKey(),
138                     // Collections.unmodifiableList( propertiesEntry.getValue() )
139                     propertiesEntry.getValue());
140         }
141         // this.properties = Collections.unmodifiableMap(properties);
142         this.properties = properties;
143     }
144 
145     /**
146      * @return map of declared properties, every property can have more than a value.
147      */
148     public Map<String, List<ItemProp>> getProperties() {
149         return properties;
150     }
151 
152     /**
153      * @return the <i>itemscope</i>
154      */
155     public String getId() {
156         return id;
157     }
158 
159     /**
160      * @return <i>itemscope</i> list of references to <i>itemprop</i>s.
161      */
162     public String[] getRefs() {
163         return refs;
164     }
165 
166     /**
167      * @return <i>itemscope</i> type.
168      */
169     public URL getType() {
170         // No longer using URL.
171         // But for backwards compatibility:
172         try {
173             return type.isEmpty() ? null : new URL(type.get(0).stringValue());
174         } catch (MalformedURLException e) {
175             try {
176                 return new URL(ParsedIRI.create(type.get(0).stringValue()).toASCIIString());
177             } catch (Exception e1) {
178                 return null;
179             }
180         }
181     }
182 
183     List<IRI> getTypes() {
184         return type;
185     }
186 
187     /**
188      * @return <i>itemscope</i> public identifier.
189      */
190     public String getItemId() {
191         return itemId;
192     }
193 
194     @Override
195     public String toJSON() {
196         StringBuilder sb = new StringBuilder();
197         int i;
198         int j;
199         final Collection<List<ItemProp>> itemPropsList = properties.values();
200         j = 0;
201         for (List<ItemProp> itemProps : itemPropsList) {
202             i = 0;
203             for (ItemProp itemProp : itemProps) {
204                 sb.append(itemProp);
205                 if (i < itemProps.size() - 1) {
206                     sb.append(", ");
207                 }
208                 i++;
209             }
210             if (j < itemPropsList.size() - 1) {
211                 sb.append(", ");
212             }
213             j++;
214         }
215         return String.format(Locale.ROOT, "{ "
216                 + "\"xpath\" : \"%s\", \"id\" : %s, \"refs\" : %s, \"type\" : %s, \"itemid\" : %s, \"properties\" : [ %s ]"
217                 + " }", getXpath(), id == null ? null : "\"" + id + "\"", refs == null ? null : toJSON(refs),
218                 type.isEmpty() ? null : "\"" + type.get(0) + "\"", itemId == null ? null : "\"" + itemId + "\"",
219                 sb.toString());
220     }
221 
222     @Override
223     public String toString() {
224         return toJSON();
225     }
226 
227     @Override
228     public int hashCode() {
229         int i = properties == null ? 0 : properties.hashCode();
230         i += id == null ? 0 : id.hashCode();
231         i += refs == null ? 0 : Arrays.hashCode(refs);
232         i += type == null ? 0 : type.hashCode();
233         i += itemId == null ? 0 : itemId.hashCode();
234         return i;
235     }
236 
237     @Override
238     public boolean equals(Object obj) {
239         if (obj == null) {
240             return false;
241         }
242         if (obj == this) {
243             return true;
244         }
245         if (obj instanceof ItemScope) {
246             final ItemScope/../../../../org/apache/any23/extractor/microdata/ItemScope.html#ItemScope">ItemScope other = (ItemScope) obj;
247             return super.getXpath().equals(other.getXpath())
248                     && (properties == null ? other.properties == null : properties.equals(other.properties))
249                     && (id == null ? other.id == null : id.equals(other.id))
250                     && (refs == null ? other.refs == null : Arrays.equals(refs, other.refs))
251                     && (type == null ? other.type == null : type.equals(other.type))
252                     && (itemId == null ? other.itemId == null : itemId.equals(other.itemId));
253         }
254         return false;
255     }
256 
257     protected void acquireProperty(ItemProp itemProp) {
258         List<ItemProp> itemProps = properties.computeIfAbsent(itemProp.getName(), k -> new ArrayList<>());
259         if (!itemProps.contains(itemProp))
260             itemProps.add(itemProp);
261     }
262 
263     protected void disownProperty(ItemProp itemProp) {
264         List<ItemProp> propList = properties.get(itemProp.getName());
265         if (propList != null)
266             propList.remove(itemProp);
267     }
268 
269     private String toJSON(String[] in) {
270         StringBuilder sb = new StringBuilder();
271         sb.append('[');
272         for (int i = 0; i < in.length; i++) {
273             sb.append("\"");
274             sb.append(in[i]);
275             sb.append("\"");
276             if (i < in.length - 1) {
277                 sb.append(", ");
278             }
279         }
280         sb.append(']');
281         return sb.toString();
282     }
283 
284 }