1   package eu.fbk.knowledgestore.data;
2   
3   import java.util.Collection;
4   import java.util.Collections;
5   import java.util.Iterator;
6   import java.util.List;
7   import java.util.NoSuchElementException;
8   
9   import javax.annotation.Nullable;
10  
11  import com.google.common.collect.AbstractIterator;
12  import com.google.common.collect.Iterators;
13  import com.google.common.collect.UnmodifiableIterator;
14  
15  import org.jaxen.DefaultNavigator;
16  import org.jaxen.UnsupportedAxisException;
17  import org.jaxen.saxpath.SAXPathException;
18  import org.openrdf.model.Statement;
19  import org.openrdf.model.URI;
20  import org.openrdf.model.Value;
21  
22  final class XPathNavigator extends DefaultNavigator {
23  
24      public static final XPathNavigator INSTANCE = new XPathNavigator();
25  
26      private static final long serialVersionUID = -1402394846594186887L;
27  
28      private XPathNavigator() {
29      }
30  
31      public Object wrap(final Object node) {
32          return new Element(null, null, node);
33      }
34  
35      public Object unwrap(final Object object) {
36          return object instanceof Element ? ((Element) object).getContent() : object;
37      }
38  
39      @Override
40      public Iterator<?> getChildAxisIterator(final Object contextNode)
41              throws UnsupportedAxisException {
42  
43          if (contextNode instanceof Element) {
44              final Element element = (Element) contextNode;
45              if (element.getContent() instanceof Record) {
46                  return new ChildIterator(element);
47              } else {
48                  final Object value = element.getContent();
49                  return Iterators.singletonIterator(value instanceof Number || //
50                          value instanceof Boolean ? value : value.toString());
51              }
52          } else {
53              return Collections.emptyIterator();
54          }
55      }
56  
57      @Override
58      public Iterator<?> getParentAxisIterator(final Object contextNode)
59              throws UnsupportedAxisException {
60          return contextNode instanceof Element ? new ParentIterator((Element) contextNode)
61                  : Collections.emptyIterator();
62      }
63  
64      @Override
65      public Object getParentNode(final Object child) throws UnsupportedAxisException {
66          return child instanceof Element ? ((Element) child).getParent() : null;
67      }
68  
69      @Override
70      public Object getDocumentNode(final Object contextNode) {
71          if (!(contextNode instanceof Element)) {
72              return null;
73          }
74          Element element = (Element) contextNode;
75          while (element.getParent() != null) {
76              element = element.getParent();
77              assert element != null;
78          }
79          return element;
80      }
81  
82      @Override
83      public String translateNamespacePrefixToUri(final String prefix, final Object element) {
84          throw new Error("This method is not expected to be called by Jaxen (!)");
85      }
86  
87      @Override
88      public String getElementNamespaceUri(final Object element) {
89          if (element instanceof Element) {
90              final URI tag = ((Element) element).getTag();
91              return tag == null ? "" : tag.getNamespace();
92          }
93          return null;
94      }
95  
96      @Override
97      public String getElementName(final Object element) {
98          if (element instanceof Element) {
99              final URI tag = ((Element) element).getTag();
100             return tag == null ? "" : tag.getLocalName();
101         }
102         return null;
103     }
104 
105     @Override
106     public String getElementQName(final Object element) {
107         throw new Error("This method is not expected to be called by Jaxen (!)");
108     }
109 
110     @Override
111     public String getAttributeNamespaceUri(final Object attr) {
112         throw new Error("This method is not expected to be called by Jaxen (!)");
113     }
114 
115     @Override
116     public String getAttributeName(final Object attr) {
117         throw new Error("This method is not expected to be called by Jaxen (!)");
118     }
119 
120     @Override
121     public String getAttributeQName(final Object attr) {
122         throw new Error("This method is not expected to be called by Jaxen (!)");
123     }
124 
125     @Override
126     public boolean isDocument(final Object object) {
127         return object instanceof Element && ((Element) object).getParent() == null;
128     }
129 
130     @Override
131     public boolean isElement(final Object object) {
132         return object instanceof Element && ((Element) object).getParent() != null;
133     }
134 
135     @Override
136     public boolean isAttribute(final Object object) {
137         return false;
138     }
139 
140     @Override
141     public boolean isNamespace(final Object object) {
142         return false;
143     }
144 
145     @Override
146     public boolean isComment(final Object object) {
147         return false;
148     }
149 
150     @Override
151     public boolean isText(final Object object) {
152         return !(object instanceof Element) && !(object instanceof Collection);
153     }
154 
155     @Override
156     public boolean isProcessingInstruction(final Object object) {
157         return false;
158     }
159 
160     @Override
161     public String getCommentStringValue(final Object comment) {
162         throw new Error("This method is not expected to be called by Jaxen (!)");
163     }
164 
165     @Override
166     public String getElementStringValue(final Object element) {
167         if (element instanceof Element) {
168             final Object object = ((Element) element).getContent();
169             if (object instanceof Record) {
170                 return "";
171             } else if (object instanceof Value) {
172                 return ((Value) object).stringValue();
173             } else if (object instanceof Statement) {
174                 return object.toString();
175             }
176         }
177         return null;
178     }
179 
180     @Override
181     public String getAttributeStringValue(final Object attr) {
182         throw new Error("This method is not expected to be called by Jaxen (!)");
183     }
184 
185     @Override
186     public String getNamespaceStringValue(final Object ns) {
187         throw new Error("This method is not expected to be called by Jaxen (!)");
188     }
189 
190     @Override
191     public String getTextStringValue(final Object text) {
192         if (text instanceof Element || text instanceof Collection) {
193             return null;
194         }
195         if (text instanceof Value) {
196             return ((Value) text).stringValue();
197         }
198         return text.toString();
199     }
200 
201     @Override
202     public String getNamespacePrefix(final Object ns) {
203         throw new Error("This method is not expected to be called by Jaxen (!)");
204     }
205 
206     @Override
207     public org.jaxen.XPath parseXPath(final String xpath) throws SAXPathException {
208         throw new Error("This method is not expected to be called by Jaxen (!)");
209     }
210 
211     private static final class ParentIterator extends UnmodifiableIterator<Object> {
212 
213         private final Element node;
214 
215         ParentIterator(final Element node) {
216             assert node != null;
217             this.node = node;
218         }
219 
220         @Override
221         public boolean hasNext() {
222             return this.node.getParent() != null;
223         }
224 
225         @Override
226         public Element next() {
227             final Element parent = this.node.getParent();
228             if (parent == null) {
229                 throw new NoSuchElementException();
230             }
231             return parent;
232         }
233 
234     }
235 
236     private static final class ChildIterator extends AbstractIterator<Object> {
237 
238         private final Element parent;
239 
240         private final List<URI> properties;
241 
242         private URI propertyURI;
243 
244         private int propertyIndex;
245 
246         private List<? extends Object> values;
247 
248         private int valueIndex;
249 
250         public ChildIterator(final Element parent) {
251 
252             assert parent != null;
253             assert parent.getContent() instanceof Record;
254 
255             this.parent = parent;
256             this.properties = ((Record) parent.getContent()).getProperties();
257             this.propertyURI = null;
258             this.propertyIndex = 0;
259             this.values = Collections.emptyList();
260             this.valueIndex = 0;
261         }
262 
263         @Override
264         protected Object computeNext() {
265             while (true) {
266                 while (this.valueIndex < this.values.size()) {
267                     final Object value = this.values.get(this.valueIndex++);
268                     // if (!BooleanLiteralImpl.FALSE.equals(value)) {
269                     // This is necessary for proper comparison of boolean values
270                     return new Element(this.parent, this.propertyURI, value);
271                     // }
272                 }
273                 if (this.propertyIndex == this.properties.size()) {
274                     return endOfData();
275                 }
276                 this.propertyURI = this.properties.get(this.propertyIndex++);
277                 this.values = ((Record) this.parent.getContent()).get(this.propertyURI);
278                 this.valueIndex = 0;
279             }
280         }
281 
282     }
283 
284     private static final class Element {
285 
286         @Nullable
287         private final Element parent;
288 
289         @Nullable
290         private final URI tag;
291 
292         private final Object content;
293 
294         public Element(@Nullable final Element parent, @Nullable final URI tag, //
295                 final Object content) {
296             this.parent = parent;
297             this.tag = tag;
298             this.content = content;
299         }
300 
301         @Nullable
302         public Element getParent() {
303             return this.parent;
304         }
305 
306         @Nullable
307         public URI getTag() {
308             return this.tag;
309         }
310 
311         public Object getContent() {
312             return this.content;
313         }
314 
315     }
316 
317 }