View Javadoc

1   // BSD License (http://www.galagosearch.org/license)
2   package org.galagosearch.core.retrieval.query;
3   
4   import java.util.ArrayList;
5   import java.util.List;
6   import java.util.Map;
7   import org.galagosearch.tupleflow.Parameters.Value;
8   import org.galagosearch.tupleflow.Parameters;
9   
10  /***
11   * <p>Node represents a single node in a query parse tree.</p>
12   * 
13   * <p>In Galago, queries are parsed into a tree of Nodes.  The query tree can then
14   * be modified using StructuredQuery.copy, or analyzed by using StructuredQuery.walk.
15   * Once the query is in the proper form, the query is converted into a tree of iterators
16   * that can be evaluated.</p>
17   * 
18   * @author trevor
19   */
20  public class Node {
21      /// The query operator represented by this node, like "combine", "weight", "syn", etc.
22      private String operator;
23  
24      /// Child nodes of the operator, e.g. in #combine(a b), 'a' and 'b' are internal nodes of #combine.
25      private ArrayList<Node> internalNodes;
26  
27      /// The position in the text string where this operator starts.  Useful for parse error messages.
28      private int position;
29  
30      /// Additional parameters for this operator; usually these are term statistics and smoothing parameters.
31      private Parameters parameters;
32  
33      public Node() {
34          internalNodes = new ArrayList<Node>();
35          parameters = new Parameters();
36      }
37  
38      public Node(String operator, ArrayList<Node> internalNodes) {
39          this(operator, (String) null, internalNodes, 0);
40      }
41  
42      public Node(String operator, ArrayList<Node> internalNodes, int position) {
43          this(operator, (String) null, internalNodes, position);
44      }
45  
46      public Node(String operator, String argument) {
47          this(operator, argument, 0);
48      }
49  
50      public Node(String operator, String argument, int position) {
51          this(operator, argument, new ArrayList<Node>(), position);
52      }
53  
54      public Node(String operator, String argument, ArrayList<Node> internalNodes) {
55          this(operator, argument, internalNodes, 0);
56      }
57  
58      public Node(String operator, String argument, ArrayList<Node> internalNodes, int position) {
59          Parameters p = new Parameters();
60  
61          if (argument != null) {
62              p.add("default", argument);
63          }
64          this.operator = operator;
65          this.internalNodes = internalNodes;
66          this.position = position;
67          this.parameters = p;
68      }
69  
70      public Node(String operator, Parameters parameters, ArrayList<Node> internalNodes, int position) {
71          this.operator = operator;
72          this.internalNodes = internalNodes;
73          this.position = position;
74          this.parameters = parameters;
75      }
76  
77      public String getOperator() {
78          return operator;
79      }
80      
81      public String getDefaultParameter() {
82          return parameters.get("default", (String)null);
83      }
84      
85      public String getDefaultParameter(String key) {
86          return parameters.get(key, getDefaultParameter());
87      }
88  
89      public ArrayList<Node> getInternalNodes() {
90          return internalNodes;
91      }
92  
93      public int getPosition() {
94          return position;
95      }
96  
97      public Parameters getParameters() {
98          return parameters;
99      }
100     
101     public boolean needsToBeEscaped(String text) {
102         return text.contains("@") || text.contains(",") ||
103                text.contains(".") || text.contains(" ") ||
104                text.contains("\t") || text.contains("\r") ||
105                text.contains("\n");
106     }
107     
108     public String escapeAsNecessary(String text) {
109         if (!needsToBeEscaped(text)) {
110             return text; 
111         } else {
112             String[] preferredDelimiters = { "/", "|", "#", "!", "%" };
113             
114             for (String delimiter : preferredDelimiters) {
115                 if (!text.contains(delimiter)) {
116                     return "@" + delimiter + text + delimiter;
117                 }
118             }
119             
120             // give up
121             return text;
122         }
123     }
124 
125     @Override
126     public String toString() {
127         StringBuilder builder = new StringBuilder();
128 
129         builder.append('#');
130         builder.append(operator);
131 
132         if (parameters.containsKey("default")) {
133             String value = parameters.get("default");
134             builder.append(':');
135             builder.append(escapeAsNecessary(value));
136         }
137 
138         Map<String, List<Value>> parameterMap = parameters.value().map();
139 
140         if (parameterMap != null) {
141             for (String key : parameterMap.keySet()) {
142                 if (key.equals("default")) {
143                     continue;
144                 }
145                 String value = parameterMap.get(key).get(0).toString();
146                 
147                 builder.append(':');
148                 builder.append(escapeAsNecessary(key));
149                 builder.append('=');
150                 builder.append(escapeAsNecessary(value));
151             }
152         }
153 
154         if (internalNodes.size() == 0) {
155             builder.append("()");
156         } else {
157             builder.append("( ");
158             for (Node child : internalNodes) {
159                 builder.append(child.toString());
160                 builder.append(' ');
161             }
162             builder.append(")");
163         }
164         
165         return builder.toString();
166     }
167 
168     @Override
169     public boolean equals(Object o) {
170         if (!(o instanceof Node)) {
171             return false;
172         }
173         if (o == this) {
174             return true;
175         }
176         Node other = (Node) o;
177 
178         if ((operator == null) != (other.getOperator() == null)) {
179             return false;
180         }
181         if (operator != null && !other.getOperator().equals(operator)) {
182             return false;
183         }
184         if (internalNodes.size() != other.getInternalNodes().size()) {
185             return false;
186         }
187         for (int i = 0; i < internalNodes.size(); i++) {
188             if (!internalNodes.get(i).equals(other.getInternalNodes().get(i))) {
189                 return false;
190             }
191         }
192 
193         return true;
194     }
195 
196     @Override
197     public int hashCode() {
198         int hash = 7;
199         hash = 67 * hash + (this.operator != null ? this.operator.hashCode() : 0);
200         hash = 67 * hash + (this.internalNodes != null ? this.internalNodes.hashCode() : 0);
201         return hash;
202     }
203 }