| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 1 | package de.ids_mannheim.korap.query.wrap; |
| 2 | |
| Eliza Margaretha | 369e756 | 2015-02-09 17:36:24 +0000 | [diff] [blame] | 3 | import java.util.ArrayList; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 4 | |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 5 | import org.apache.lucene.index.Term; |
| Eliza Margaretha | 369e756 | 2015-02-09 17:36:24 +0000 | [diff] [blame] | 6 | import org.apache.lucene.search.spans.SpanQuery; |
| 7 | import org.apache.lucene.search.spans.SpanTermQuery; |
| Nils Diewald | dc8dc34 | 2014-07-25 13:38:50 +0000 | [diff] [blame] | 8 | import org.slf4j.Logger; |
| 9 | import org.slf4j.LoggerFactory; |
| 10 | |
| Eliza Margaretha | 369e756 | 2015-02-09 17:36:24 +0000 | [diff] [blame] | 11 | import de.ids_mannheim.korap.query.DistanceConstraint; |
| 12 | import de.ids_mannheim.korap.query.SpanDistanceQuery; |
| 13 | import de.ids_mannheim.korap.query.SpanElementQuery; |
| 14 | import de.ids_mannheim.korap.query.SpanExpansionQuery; |
| 15 | import de.ids_mannheim.korap.query.SpanMultipleDistanceQuery; |
| 16 | import de.ids_mannheim.korap.query.SpanNextQuery; |
| 17 | import de.ids_mannheim.korap.util.QueryException; |
| 18 | |
| Nils Diewald | cc7c0b3 | 2014-07-31 19:58:22 +0000 | [diff] [blame] | 19 | /* |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 20 | TODO: Make isNegative work! |
| 21 | TODO: Make isEmpty work! |
| 22 | TODO: Make isExtendedToTheRight work! |
| 23 | TODO: Evaluate if spanNext(spanNext(a,b),spanNext(c,d)) is faster |
| 24 | than spanNext(spanNext(spanNext(a,b),c),d) |
| 25 | TODO: Improve support for SpanElementQueryWrapper in constraints! |
| Nils Diewald | cc7c0b3 | 2014-07-31 19:58:22 +0000 | [diff] [blame] | 26 | */ |
| 27 | |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 28 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 29 | * Deserialize complexe sequence queries to SpanQueries. |
| 30 | * This will try to make queries work, that by simple nesting won't |
| 31 | * (like queries with empty sequences), and will optimize queries |
| 32 | * if possible. |
| Nils Diewald | 92729ce | 2014-10-06 16:00:17 +0000 | [diff] [blame] | 33 | * |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 34 | * Todo: Synopsis |
| 35 | * |
| 36 | * @author diewald |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 37 | */ |
| Nils Diewald | 92729ce | 2014-10-06 16:00:17 +0000 | [diff] [blame] | 38 | public class SpanSequenceQueryWrapper extends SpanQueryWrapper { |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 39 | private String field; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 40 | private ArrayList<SpanQueryWrapper> segments; |
| Nils Diewald | d7cb0eb | 2014-02-12 23:06:10 +0000 | [diff] [blame] | 41 | private ArrayList<DistanceConstraint> constraints; |
| Nils Diewald | dc8dc34 | 2014-07-25 13:38:50 +0000 | [diff] [blame] | 42 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 43 | private QueryException constraintException = null; |
| 44 | |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 45 | private final String limitationError = |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 46 | "Distance constraints not supported with " + |
| 47 | "empty or negative operands"; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 48 | |
| Nils Diewald | dc8dc34 | 2014-07-25 13:38:50 +0000 | [diff] [blame] | 49 | // Logger |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 50 | private final static Logger log = |
| 51 | LoggerFactory.getLogger(SpanSequenceQueryWrapper.class); |
| Nils Diewald | dc8dc34 | 2014-07-25 13:38:50 +0000 | [diff] [blame] | 52 | |
| 53 | // This advices the java compiler to ignore all loggings |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 54 | public static final boolean DEBUG = false; |
| Nils Diewald | d7cb0eb | 2014-02-12 23:06:10 +0000 | [diff] [blame] | 55 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 56 | private boolean isInOrder = true; |
| 57 | |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 58 | // The sequence is problem solved |
| 59 | private boolean isSolved = false; |
| 60 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 61 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 62 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 63 | * Constructs a new object for sequence deserialization. |
| 64 | * |
| 65 | * @param field The fields the nested SpanQueries should |
| 66 | * search in. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 67 | */ |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 68 | public SpanSequenceQueryWrapper (String field) { |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 69 | this.field = field; |
| 70 | this.segments = new ArrayList<SpanQueryWrapper>(2); |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 71 | }; |
| 72 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 73 | |
| 74 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 75 | * Constructs a new object for sequence deserialization |
| 76 | * by passing a sequence of terms. |
| 77 | * |
| 78 | * <blockquote><pre> |
| 79 | * SpanSequenceQueryWrapper ssqw = |
| 80 | * new SpanSequenceQueryWrapper("tokens", "der", "Baum"); |
| 81 | * System.out.println(ssqw.toQuery()); |
| 82 | * // spanNext(tokens:der, tokens:Baum) |
| 83 | * </pre></blockquote> |
| 84 | * |
| 85 | * @param field The fields the nested SpanQueries should |
| 86 | * search in. |
| 87 | * @param terms[] Arbitrary list of terms to search for. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 88 | */ |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 89 | public SpanSequenceQueryWrapper (String field, String ... terms) { |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 90 | this(field); |
| 91 | for (int i = 0; i < terms.length; i++) { |
| 92 | this.segments.add( |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 93 | new SpanSimpleQueryWrapper( |
| 94 | new SpanTermQuery(new Term(field, terms[i])) |
| 95 | ) |
| 96 | ); |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 97 | }; |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 98 | // Query can't be null anymore |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 99 | this.isNull = false; |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 100 | }; |
| 101 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 102 | |
| 103 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 104 | * Constructs a new object for sequence deserialization |
| 105 | * by passing a single {@link SpanQuery} object. |
| 106 | * |
| 107 | * @param query Initial {@link SpanQuery} to search for. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 108 | */ |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 109 | public SpanSequenceQueryWrapper (SpanQuery query) { |
| 110 | this(query.getField()); |
| 111 | this.segments.add(new SpanSimpleQueryWrapper(query)); |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 112 | this.isNull = false; |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 113 | }; |
| 114 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 115 | |
| 116 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 117 | * Constructs a new object for sequence deserialization |
| 118 | * by passing a single {@link SpanQueryWrapper} object. |
| 119 | * These wrapper queries may be optional, negative, or empty. |
| 120 | * |
| 121 | * @param query Initial {@link SpanQueryWrapper} to search for. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 122 | */ |
| Nils Diewald | 92729ce | 2014-10-06 16:00:17 +0000 | [diff] [blame] | 123 | public SpanSequenceQueryWrapper (String field, SpanQueryWrapper sswq) { |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 124 | this(field); |
| Nils Diewald | cc7c0b3 | 2014-07-31 19:58:22 +0000 | [diff] [blame] | 125 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 126 | // Ignore null queries |
| 127 | if (sswq.isNull()) |
| 128 | return; |
| Nils Diewald | cc7c0b3 | 2014-07-31 19:58:22 +0000 | [diff] [blame] | 129 | |
| Nils Diewald | 0981c21 | 2015-02-13 20:47:10 +0000 | [diff] [blame] | 130 | if (sswq.maybeUnsorted()) |
| 131 | this.maybeUnsorted = true; |
| 132 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 133 | // Some debugging on initiating new sequences |
| 134 | if (DEBUG) { |
| 135 | if (!sswq.isEmpty()) { |
| 136 | try { |
| 137 | log.trace("New span sequence {}", sswq.toQuery().toString()); |
| 138 | } |
| 139 | catch (QueryException qe) { |
| 140 | log.trace("Unable to serialize query {}", qe.getMessage()); |
| 141 | }; |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 142 | } |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 143 | else { |
| 144 | log.trace("New span sequence, that's initially empty"); |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 145 | }; |
| 146 | }; |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 147 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 148 | this.segments.add(sswq); |
| 149 | this.isNull = false; |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 150 | }; |
| 151 | |
| Nils Diewald | 6b33281 | 2014-07-22 18:51:05 +0000 | [diff] [blame] | 152 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 153 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 154 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 155 | * Append a new term to the sequence. |
| 156 | * |
| 157 | * <blockquote><pre> |
| 158 | * SpanSequenceQueryWrapper ssqw = |
| 159 | * new SpanSequenceQueryWrapper("tokens"); |
| 160 | * ssqw.append("der").append("Baum"); |
| 161 | * System.out.println(ssqw.toQuery()); |
| 162 | * // spanNext(tokens:der, tokens:Baum) |
| 163 | * </pre></blockquote> |
| 164 | * |
| 165 | * @param term A new string to search for. |
| 166 | * @return The {@link SpanSequenceQueryWrapper} object for chaining. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 167 | */ |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 168 | public SpanSequenceQueryWrapper append (String term) { |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 169 | return this.append(new SpanTermQuery(new Term(field, term))); |
| Nils Diewald | 602c922 | 2014-07-23 19:49:53 +0000 | [diff] [blame] | 170 | }; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 171 | |
| 172 | |
| 173 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 174 | * Append a new {@link SpanQuery} object to the sequence. |
| 175 | * |
| 176 | * @param query A new {@link SpanQuery} to search for. |
| 177 | * @return The {@link SpanSequenceQueryWrapper} object for chaining. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 178 | */ |
| Nils Diewald | 602c922 | 2014-07-23 19:49:53 +0000 | [diff] [blame] | 179 | public SpanSequenceQueryWrapper append (SpanQuery query) { |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 180 | return this.append(new SpanSimpleQueryWrapper(query)); |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 181 | }; |
| 182 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 183 | |
| 184 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 185 | * Append a new {@link SpanQueryWrapper} object to the sequence. |
| 186 | * |
| 187 | * @param query A new {@link SpanQueryWrapper} to search for. |
| 188 | * @return The {@link SpanSequenceQueryWrapper} object for chaining. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 189 | */ |
| Eliza Margaretha | 369e756 | 2015-02-09 17:36:24 +0000 | [diff] [blame] | 190 | public SpanSequenceQueryWrapper append(SpanQueryWrapper ssq) { |
| Nils Diewald | 92729ce | 2014-10-06 16:00:17 +0000 | [diff] [blame] | 191 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 192 | // The wrapper is null - ignore this in the sequence |
| 193 | if (ssq.isNull()) |
| 194 | return this; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 195 | |
| Nils Diewald | 0981c21 | 2015-02-13 20:47:10 +0000 | [diff] [blame] | 196 | if (ssq.maybeUnsorted()) |
| 197 | this.maybeUnsorted = true; |
| 198 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 199 | // As the spanQueryWrapper is not null, |
| 200 | // the sequence can't be null as well |
| 201 | this.isNull = false; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 202 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 203 | // The sequence may be problematic |
| 204 | this.isSolved = false; |
| Nils Diewald | b84e727 | 2014-11-07 01:27:38 +0000 | [diff] [blame] | 205 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 206 | // Embed a nested sequence |
| 207 | if (ssq instanceof SpanSequenceQueryWrapper) { |
| Nils Diewald | b84e727 | 2014-11-07 01:27:38 +0000 | [diff] [blame] | 208 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 209 | if (DEBUG) |
| 210 | log.trace("Add SpanSequenceQueryWrapper to sequence"); |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 211 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 212 | // Some casting |
| 213 | SpanSequenceQueryWrapper ssqw = (SpanSequenceQueryWrapper) ssq; |
| Nils Diewald | 92729ce | 2014-10-06 16:00:17 +0000 | [diff] [blame] | 214 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 215 | // There are no constraints and the order is equal - Flatten! |
| 216 | if (!this.hasConstraints() && !ssqw.hasConstraints() && |
| 217 | this.isInOrder() == ssqw.isInOrder()) { |
| 218 | for (int i = 0; i < ssqw.segments.size(); i++) { |
| 219 | this.append(ssqw.segments.get(i)); |
| 220 | }; |
| 221 | } |
| 222 | |
| 223 | // Unable to flatten ... :-( |
| 224 | else { |
| 225 | this.segments.add(ssq); |
| 226 | }; |
| 227 | } |
| 228 | |
| 229 | // This is not a sequence |
| 230 | else { |
| 231 | this.segments.add(ssq); |
| 232 | }; |
| 233 | |
| 234 | return this; |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 235 | }; |
| 236 | |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 237 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 238 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 239 | * Prepend a new term to the sequence. |
| 240 | * |
| 241 | * <blockquote><pre> |
| 242 | * SpanSequenceQueryWrapper ssqw = |
| 243 | * new SpanSequenceQueryWrapper("tokens", "Baum"); |
| 244 | * ssqw.prepend("der"); |
| 245 | * System.out.println(ssqw.toQuery()); |
| 246 | * // spanNext(tokens:der, tokens:Baum) |
| 247 | * </pre></blockquote> |
| 248 | * |
| 249 | * @param term A new string to search for. |
| 250 | * @return The {@link SpanSequenceQueryWrapper} object for chaining. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 251 | */ |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 252 | public SpanSequenceQueryWrapper prepend (String term) { |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 253 | return this.prepend(new SpanTermQuery(new Term(field, term))); |
| Nils Diewald | 602c922 | 2014-07-23 19:49:53 +0000 | [diff] [blame] | 254 | }; |
| 255 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 256 | |
| 257 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 258 | * Prepend a new {@link SpanQuery} object to the sequence. |
| 259 | * |
| 260 | * @param query A new {@link SpanQuery} to search for. |
| 261 | * @return The {@link SpanSequenceQueryWrapper} object for chaining. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 262 | */ |
| Nils Diewald | 602c922 | 2014-07-23 19:49:53 +0000 | [diff] [blame] | 263 | public SpanSequenceQueryWrapper prepend (SpanQuery query) { |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 264 | return this.prepend(new SpanSimpleQueryWrapper(query)); |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 265 | }; |
| 266 | |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 267 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 268 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 269 | * Prepend a new {@link SpanQueryWrapper} object to the sequence. |
| 270 | * |
| 271 | * @param query A new {@link SpanQueryWrapper} to search for. |
| 272 | * @return The {@link SpanSequenceQueryWrapper} object for chaining. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 273 | */ |
| Nils Diewald | 92729ce | 2014-10-06 16:00:17 +0000 | [diff] [blame] | 274 | public SpanSequenceQueryWrapper prepend (SpanQueryWrapper ssq) { |
| Nils Diewald | cc7c0b3 | 2014-07-31 19:58:22 +0000 | [diff] [blame] | 275 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 276 | // The wrapper is null - ignore this in the sequence |
| 277 | if (ssq.isNull()) |
| 278 | return this; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 279 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 280 | // As the spanQueryWrapper is not null, |
| 281 | // the sequence can't be null as well |
| 282 | this.isNull = false; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 283 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 284 | // The sequence may be problematic |
| 285 | this.isSolved = false; |
| Nils Diewald | b84e727 | 2014-11-07 01:27:38 +0000 | [diff] [blame] | 286 | |
| Nils Diewald | 0981c21 | 2015-02-13 20:47:10 +0000 | [diff] [blame] | 287 | if (ssq.maybeUnsorted()) |
| 288 | this.maybeUnsorted = true; |
| 289 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 290 | // Embed a nested sequence |
| 291 | if (ssq instanceof SpanSequenceQueryWrapper) { |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 292 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 293 | // There are no constraints and the order is equal - Flatten! |
| 294 | SpanSequenceQueryWrapper ssqw = (SpanSequenceQueryWrapper) ssq; |
| 295 | if (!this.hasConstraints() && |
| 296 | !ssqw.hasConstraints() && |
| 297 | this.isInOrder() == ssqw.isInOrder()) { |
| 298 | for (int i = ssqw.segments.size() - 1; i >= 0; i--) { |
| 299 | this.prepend(ssqw.segments.get(i)); |
| 300 | }; |
| 301 | } |
| Nils Diewald | ff631ae | 2014-07-24 13:03:17 +0000 | [diff] [blame] | 302 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 303 | // Unable to flatten ... :-( |
| 304 | else { |
| 305 | this.segments.add(0, ssq); |
| 306 | }; |
| 307 | } |
| 308 | |
| 309 | // This is not a sequence |
| 310 | else { |
| 311 | this.segments.add(0, ssq); |
| 312 | }; |
| 313 | |
| 314 | return this; |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 315 | }; |
| 316 | |
| Nils Diewald | 6b33281 | 2014-07-22 18:51:05 +0000 | [diff] [blame] | 317 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 318 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 319 | * Add a token based sequence constraint (aka distance constraint) |
| 320 | * to the sequence. |
| 321 | * |
| 322 | * Multiple constraints are supported. |
| 323 | * |
| 324 | * A minimum value of zero means, there may be an overlap, |
| 325 | * a minimum value of 1 means, there is no token between the spans. |
| 326 | * It's weird - we know and dislike that. That's why we have to say: |
| 327 | * |
| 328 | * <strong>Warning!</strong> Sequence constraints are experimental and |
| 329 | * may (hopefully) change in future versions! |
| 330 | * |
| 331 | * @param min The minimum number of tokens between the elements |
| 332 | * of the sequence. |
| 333 | * @param max The minimum number of tokens between the elements |
| 334 | * of the sequence. |
| 335 | * @return The {@link SpanSequenceQueryWrapper} object for chaining. |
| 336 | * @see DistanceConstraint |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 337 | */ |
| Nils Diewald | d7cb0eb | 2014-02-12 23:06:10 +0000 | [diff] [blame] | 338 | public SpanSequenceQueryWrapper withConstraint (int min, int max) { |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 339 | return this.withConstraint(min, max, false); |
| Nils Diewald | 8c54343 | 2014-02-27 18:25:38 +0000 | [diff] [blame] | 340 | }; |
| 341 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 342 | |
| 343 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 344 | * Add a token based sequence constraint (aka distance constraint) |
| 345 | * to the sequence with an exclusion constraint, meaning |
| 346 | * the constraint is fine in case the operands are <em>not</em> |
| 347 | * within the distance. |
| 348 | * |
| 349 | * Multiple constraints are supported. |
| 350 | * |
| 351 | * A minimum value of zero means, there may be an overlap, |
| 352 | * a minimum value of 1 means, there is no token between the spans. |
| 353 | * It's weird - we know and dislike that. That's why we have to say: |
| 354 | * |
| 355 | * <strong>Warning!</strong> Sequence constraints are experimental and |
| 356 | * may (hopefully) change in future versions! |
| 357 | * |
| 358 | * @param min The minimum number of tokens between the elements |
| 359 | * of the sequence. |
| 360 | * @param max The minimum number of tokens between the elements |
| 361 | * of the sequence. |
| 362 | * @param exclusion Boolean value indicating, the distance constraint |
| 363 | * has to fail. |
| 364 | * @return The {@link SpanSequenceQueryWrapper} object for chaining. |
| 365 | * @see DistanceConstraint |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 366 | */ |
| Nils Diewald | 8c54343 | 2014-02-27 18:25:38 +0000 | [diff] [blame] | 367 | public SpanSequenceQueryWrapper withConstraint (int min, int max, boolean exclusion) { |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 368 | if (this.constraints == null) |
| 369 | this.constraints = new ArrayList<DistanceConstraint>(1); |
| 370 | this.constraints.add(new DistanceConstraint(min, max, this.isInOrder, exclusion)); |
| 371 | return this; |
| Nils Diewald | d7cb0eb | 2014-02-12 23:06:10 +0000 | [diff] [blame] | 372 | }; |
| 373 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 374 | |
| 375 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 376 | * Add a sequence constraint (aka distance constraint) |
| 377 | * to the sequence based on a certain unit. |
| 378 | * The unit has to be a valid {@link SpanElementQuery} term |
| 379 | * or <tt>w</tt> for tokens. |
| 380 | * |
| 381 | * Multiple constraints are supported. |
| 382 | * |
| 383 | * A minimum value of zero means, there may be an overlap, |
| 384 | * a minimum value of 1 means, there is no token between the spans. |
| 385 | * It's weird - we know and dislike that. That's why we have to say: |
| 386 | * |
| 387 | * <strong>Warning!</strong> Sequence constraints are experimental and |
| 388 | * may (hopefully) change in future versions! |
| 389 | * |
| 390 | * @param min The minimum number of tokens between the elements |
| 391 | * of the sequence. |
| 392 | * @param max The minimum number of tokens between the elements |
| 393 | * of the sequence. |
| 394 | * @param unit Unit for distance - will be evaluated to a {@link SpanElementQuery}. |
| 395 | * @return The {@link SpanSequenceQueryWrapper} object for chaining. |
| 396 | * @see DistanceConstraint |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 397 | */ |
| Nils Diewald | 164f8be | 2014-02-13 02:43:16 +0000 | [diff] [blame] | 398 | public SpanSequenceQueryWrapper withConstraint (int min, int max, String unit) { |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 399 | return this.withConstraint(min, max, unit, false); |
| Nils Diewald | 8c54343 | 2014-02-27 18:25:38 +0000 | [diff] [blame] | 400 | }; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 401 | |
| Nils Diewald | 8c54343 | 2014-02-27 18:25:38 +0000 | [diff] [blame] | 402 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 403 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 404 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 405 | * Add a sequence constraint (aka distance constraint) |
| 406 | * to the sequence based on a certain unit and with an |
| 407 | * exclusion constraint, meaning the constraint is fine |
| 408 | * in case the operands are <em>not</em> within the distance. |
| 409 | * The unit has to be a valid {@link SpanElementQuery} term |
| 410 | * or <tt>w</tt> for tokens. |
| 411 | * |
| 412 | * Multiple constraints are supported. |
| 413 | * |
| 414 | * A minimum value of zero means, there may be an overlap, |
| 415 | * a minimum value of 1 means, there is no token between the spans. |
| 416 | * It's weird - we know and dislike that. That's why we have to say: |
| 417 | * |
| 418 | * <strong>Warning!</strong> Sequence constraints are experimental and |
| 419 | * may (hopefully) change in future versions! |
| 420 | * |
| 421 | * @param min The minimum number of tokens between the elements |
| 422 | * of the sequence. |
| 423 | * @param max The minimum number of tokens between the elements |
| 424 | * of the sequence. |
| 425 | * @param unit Unit for distance - will be evaluated to a {@link SpanElementQuery}. |
| 426 | * @param exclusion Boolean value indicating, the distance constraint |
| 427 | * has to fail. |
| 428 | * @return The {@link SpanSequenceQueryWrapper} object for chaining. |
| 429 | * @see DistanceConstraint |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 430 | */ |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 431 | public SpanSequenceQueryWrapper withConstraint |
| 432 | (int min, int max, String unit, boolean exclusion) { |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 433 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 434 | // Word unit |
| 435 | if (unit.equals("w")) { |
| 436 | if (this.constraints == null) |
| 437 | this.constraints = new ArrayList<DistanceConstraint>(1); |
| 438 | this.constraints.add(new DistanceConstraint(min, max, isInOrder, exclusion)); |
| 439 | return this; |
| 440 | }; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 441 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 442 | // Element unit (sentence or paragraph) |
| 443 | return this.withConstraint( |
| 444 | min, max, new SpanElementQueryWrapper(this.field, unit), exclusion |
| 445 | ); |
| Nils Diewald | d7cb0eb | 2014-02-12 23:06:10 +0000 | [diff] [blame] | 446 | }; |
| 447 | |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 448 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 449 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 450 | * Add a sequence constraint (aka distance constraint) |
| 451 | * to the sequence based on a certain unit and with an |
| 452 | * exclusion constraint, meaning the constraint is fine |
| 453 | * in case the operands are <em>not</em> within the distance. |
| 454 | * |
| 455 | * Multiple constraints are supported. |
| 456 | * |
| 457 | * A minimum value of zero means, there may be an overlap, |
| 458 | * a minimum value of 1 means, there is no token between the spans. |
| 459 | * It's weird - we know and dislike that. That's why we have to say: |
| 460 | * |
| 461 | * <strong>Warning!</strong> Sequence constraints are experimental and |
| 462 | * may (hopefully) change in future versions! |
| 463 | * |
| 464 | * @param min The minimum number of tokens between the elements |
| 465 | * of the sequence. |
| 466 | * @param max The minimum number of tokens between the elements |
| 467 | * of the sequence. |
| 468 | * @param unit A {@link SpanElementQueryWrapper} as the unit for distance. |
| 469 | * @param exclusion Boolean value indicating, the distance constraint |
| 470 | * has to fail. |
| 471 | * @return The {@link SpanSequenceQueryWrapper} object for chaining. |
| 472 | * @see DistanceConstraint |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 473 | */ |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 474 | public SpanSequenceQueryWrapper withConstraint |
| 475 | (int min, int max, SpanElementQueryWrapper unit, boolean exclusion) { |
| 476 | if (this.constraints == null) |
| 477 | this.constraints = new ArrayList<DistanceConstraint>(1); |
| 478 | |
| 479 | // Element unit (sentence or paragraph) |
| 480 | // Todo: This should possibly be evaluated to a query later on! |
| 481 | try { |
| 482 | this.constraints.add( |
| 483 | new DistanceConstraint( |
| Nils Diewald | b9dd413 | 2015-02-16 16:32:41 +0000 | [diff] [blame^] | 484 | (SpanElementQuery) unit.retrieveNode(this.retrieveNode).toQuery(), |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 485 | min, |
| 486 | max, |
| 487 | isInOrder, |
| 488 | exclusion |
| 489 | ) |
| 490 | ); |
| 491 | } |
| 492 | catch (QueryException qe) { |
| 493 | this.constraintException = qe; |
| 494 | }; |
| 495 | return this; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 496 | }; |
| 497 | |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 498 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 499 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 500 | * Check if the sequence has to be in order. |
| 501 | * |
| 502 | * @return <tt>true</tt> in case the sequence |
| 503 | * has to be in order and <tt>false</tt> |
| 504 | * in case the order is not relevant. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 505 | */ |
| 506 | public boolean isInOrder () { |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 507 | return this.isInOrder; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 508 | }; |
| 509 | |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 510 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 511 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 512 | * Set the boolean value indicating if the sequence |
| 513 | * has to be in order. |
| 514 | * |
| 515 | * @param order <tt>true</tt> in case the sequence |
| 516 | * has to be in order and <tt>false</tt> |
| 517 | * in case the order is not relevant. |
| 518 | */ |
| 519 | public void setInOrder (boolean order) { |
| 520 | this.isInOrder = order; |
| 521 | }; |
| 522 | |
| 523 | |
| 524 | /** |
| 525 | * Check if the sequence has constraints. |
| 526 | * |
| 527 | * @return <tt>true</tt> in case the sequence |
| 528 | * has any constraints and <tt>false</tt> |
| 529 | * in case it is a simple next query. |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 530 | */ |
| 531 | public boolean hasConstraints () { |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 532 | if (this.constraints == null) |
| 533 | return false; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 534 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 535 | if (this.constraints.size() <= 0) |
| 536 | return false; |
| 537 | |
| 538 | // The constraint is in fact a next query, |
| 539 | // that will be optimized away later on |
| 540 | if (this.constraints.size() == 1) { |
| 541 | DistanceConstraint dc = this.constraints.get(0); |
| 542 | if (dc.getUnit().equals("w") && |
| 543 | dc.getMinDistance() == 1 && |
| 544 | dc.getMaxDistance() == 1) { |
| 545 | return false; |
| 546 | }; |
| 547 | }; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 548 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 549 | return true; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 550 | }; |
| 551 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 552 | |
| 553 | public boolean isEmpty () { |
| 554 | if (this.segments.size() == 1) |
| 555 | return this.segments.get(0).isEmpty(); |
| 556 | |
| 557 | if (!this.isSolved) |
| 558 | _solveProblematicSequence(); |
| 559 | return super.isEmpty(); |
| 560 | }; |
| 561 | |
| 562 | |
| 563 | public boolean isOptional () { |
| 564 | if (this.segments.size() == 1) |
| 565 | return this.segments.get(0).isOptional(); |
| 566 | if (!this.isSolved) |
| 567 | _solveProblematicSequence(); |
| 568 | return super.isOptional(); |
| 569 | }; |
| 570 | |
| 571 | public boolean isNegative () { |
| 572 | if (this.segments.size() == 1) |
| 573 | return this.segments.get(0).isNegative(); |
| 574 | if (!this.isSolved) |
| 575 | _solveProblematicSequence(); |
| 576 | return super.isNegative(); |
| 577 | }; |
| 578 | |
| 579 | public boolean isExtendedToTheRight () { |
| 580 | if (!this.isSolved) |
| 581 | _solveProblematicSequence(); |
| 582 | return this.isExtendedToTheRight; |
| 583 | }; |
| 584 | |
| 585 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 586 | /** |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 587 | * Serialize the wrapped sequence to a {@link SpanQuery} object. |
| 588 | * |
| 589 | * @return A {@link SpanQuery} object. |
| 590 | * @throws QueryException |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 591 | */ |
| 592 | public SpanQuery toQuery () throws QueryException { |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 593 | |
| 594 | // There was a serialization failure not yet reported |
| 595 | if (this.constraintException != null) |
| 596 | throw constraintException; |
| 597 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 598 | int size = this.segments.size(); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 599 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 600 | // Nothing to do |
| 601 | if (size == 0 || this.isNull()) |
| 602 | return (SpanQuery) null; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 603 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 604 | // No real sequence - only one element |
| 605 | if (size == 1) { |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 606 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 607 | // But the element may be expanded |
| 608 | if (this.segments.get(0).isExtended() && |
| 609 | (this.hasConstraints() || !this.isInOrder())) { |
| 610 | throw new QueryException(613, limitationError); |
| 611 | }; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 612 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 613 | // Unproblematic single query |
| 614 | if (this.segments.get(0).maybeAnchor()) |
| Nils Diewald | b9dd413 | 2015-02-16 16:32:41 +0000 | [diff] [blame^] | 615 | return (SpanQuery) this.segments.get(0).retrieveNode(this.retrieveNode).toQuery(); |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 616 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 617 | if (this.segments.get(0).isEmpty()) |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 618 | throw new QueryException( |
| 619 | 613, "Sequence is not allowed to be empty" |
| 620 | ); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 621 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 622 | if (this.segments.get(0).isOptional()) |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 623 | throw new QueryException( |
| 624 | 613, "Sequence is not allowed to be optional" |
| 625 | ); |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 626 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 627 | if (this.segments.get(0).isNegative()) |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 628 | throw new QueryException( |
| 629 | 613, "Sequence is not allowed to be negative" |
| 630 | ); |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 631 | }; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 632 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 633 | if (!this.isSolved) { |
| 634 | if (!_solveProblematicSequence()) { |
| 635 | if (this.segments.get(0).maybeExtension()) { |
| 636 | throw new QueryException( |
| 637 | 613, |
| 638 | "Sequence contains unresolvable " + |
| 639 | "empty, optional, or negative segments" |
| 640 | ); |
| 641 | }; |
| 642 | }; |
| 643 | }; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 644 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 645 | // The element may be expanded |
| 646 | if (this.segments.size() == 1 && |
| 647 | this.segments.get(0).isExtended() && |
| 648 | (this.hasConstraints() || !this.isInOrder())) { |
| 649 | throw new QueryException(613, limitationError); |
| 650 | }; |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 651 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 652 | // Create the initial query |
| 653 | SpanQuery query = null;// = this.segments.get(0).toQuery(); |
| 654 | int i = 0; |
| 655 | while (query == null && i < this.segments.size()) { |
| Nils Diewald | b9dd413 | 2015-02-16 16:32:41 +0000 | [diff] [blame^] | 656 | query = this.segments.get(i).retrieveNode(this.retrieveNode).toQuery(); |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 657 | i++; |
| 658 | }; |
| Nils Diewald | d7cb0eb | 2014-02-12 23:06:10 +0000 | [diff] [blame] | 659 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 660 | if (query == null) |
| 661 | return (SpanQuery) null; |
| Nils Diewald | 164f8be | 2014-02-13 02:43:16 +0000 | [diff] [blame] | 662 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 663 | // NextQueries: |
| 664 | if (!this.hasConstraints() && this.isInOrder()) { |
| 665 | for (; i < this.segments.size(); i++) { |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 666 | |
| Nils Diewald | b9dd413 | 2015-02-16 16:32:41 +0000 | [diff] [blame^] | 667 | SpanQuery second = this.segments.get(i).retrieveNode(this.retrieveNode).toQuery(); |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 668 | if (second == null) |
| 669 | continue; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 670 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 671 | query = new SpanNextQuery( |
| 672 | query, |
| 673 | second |
| 674 | ); |
| 675 | }; |
| 676 | return (SpanQuery) query; |
| 677 | }; |
| 678 | |
| 679 | // DistanceQueries |
| 680 | if (this.constraints.size() == 1) { |
| 681 | DistanceConstraint constraint = this.constraints.get(0); |
| Nils Diewald | 164f8be | 2014-02-13 02:43:16 +0000 | [diff] [blame] | 682 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 683 | // Create spanElementDistance query |
| 684 | if (!constraint.getUnit().equals("w")) { |
| 685 | for (i = 1; i < this.segments.size(); i++) { |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 686 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 687 | // No support for extended spans in constraints |
| 688 | if (this.segments.get(i).isExtended()) |
| 689 | throw new QueryException(613, limitationError); |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 690 | |
| Nils Diewald | b9dd413 | 2015-02-16 16:32:41 +0000 | [diff] [blame^] | 691 | SpanQuery sq = (SpanQuery) this.segments.get(i).retrieveNode(this.retrieveNode).toQuery(); |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 692 | if (sq == null) continue; |
| Nils Diewald | 164f8be | 2014-02-13 02:43:16 +0000 | [diff] [blame] | 693 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 694 | SpanDistanceQuery sdquery = new SpanDistanceQuery( |
| 695 | query, |
| 696 | sq, |
| 697 | constraint, |
| 698 | true |
| 699 | ); |
| 700 | query = (SpanQuery) sdquery; |
| 701 | }; |
| 702 | } |
| Nils Diewald | d7cb0eb | 2014-02-12 23:06:10 +0000 | [diff] [blame] | 703 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 704 | // Create spanDistance query |
| 705 | else { |
| 706 | for (i = 1; i < this.segments.size(); i++) { |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 707 | |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 708 | // No support for extended spans in constraints |
| 709 | if (this.segments.get(i).isExtended()) |
| 710 | throw new QueryException(613, limitationError); |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 711 | |
| Nils Diewald | b9dd413 | 2015-02-16 16:32:41 +0000 | [diff] [blame^] | 712 | SpanQuery sq = (SpanQuery) this.segments.get(i).retrieveNode(this.retrieveNode).toQuery(); |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 713 | if (sq == null) continue; |
| 714 | |
| 715 | SpanDistanceQuery sdquery = new SpanDistanceQuery( |
| 716 | query, |
| 717 | sq, |
| 718 | constraint, |
| 719 | true |
| 720 | ); |
| 721 | query = (SpanQuery) sdquery; |
| 722 | }; |
| 723 | }; |
| 724 | |
| 725 | return (SpanQuery) query; |
| 726 | }; |
| 727 | |
| 728 | // MultipleDistanceQueries |
| 729 | for (i = 1; i < this.segments.size(); i++) { |
| 730 | |
| 731 | // No support for extended spans in constraints |
| 732 | if (this.segments.get(i).isExtended()) |
| 733 | throw new QueryException(613, limitationError); |
| 734 | |
| Nils Diewald | b9dd413 | 2015-02-16 16:32:41 +0000 | [diff] [blame^] | 735 | SpanQuery sq = (SpanQuery) this.segments.get(i).retrieveNode(this.retrieveNode).toQuery(); |
| Nils Diewald | 6409a92 | 2015-01-29 20:50:42 +0000 | [diff] [blame] | 736 | if (sq == null) continue; |
| 737 | |
| 738 | query = new SpanMultipleDistanceQuery( |
| 739 | query, |
| 740 | sq, |
| 741 | this.constraints, |
| 742 | isInOrder, |
| 743 | true |
| 744 | ); |
| 745 | }; |
| 746 | return (SpanQuery) query; |
| Nils Diewald | d7cb0eb | 2014-02-12 23:06:10 +0000 | [diff] [blame] | 747 | }; |
| 748 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 749 | |
| 750 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 751 | /* |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 752 | Check if there are problematic segments in the sequence |
| 753 | (either negative, optional or empty) and deal with them |
| 754 | (make optional segments to or-queries and negative and empty |
| 755 | segments to extensions). |
| 756 | This has to be done as long as there are problematic segments |
| 757 | In the queries. |
| 758 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 759 | While there is a segment isNegative() or isOptional() or isEmpty() do |
| 760 | - look for an anchor next to it |
| 761 | - merge the problematic segment with the anchor |
| 762 | - go on |
| 763 | */ |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 764 | private boolean _solveProblematicSequence () { |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 765 | int size = this.segments.size(); |
| 766 | // Check if there is a problematic segment |
| 767 | SpanQueryWrapper underScrutiny; |
| 768 | boolean noRemainingProblem = true; |
| 769 | int i = 0; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 770 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 771 | if (DEBUG) |
| 772 | log.trace("Try to solve a query of {} segments", size); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 773 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 774 | // Iterate over all segments |
| 775 | for (; i < size;) { |
| 776 | underScrutiny = this.segments.get(i); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 777 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 778 | // Check if there is a problem with the current segment |
| 779 | if (!underScrutiny.maybeAnchor()) { |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 780 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 781 | if (DEBUG) |
| 782 | log.trace("segment {} is problematic", i); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 783 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 784 | // [problem][anchor] |
| 785 | if (i < (size-1) && this.segments.get(i+1).maybeAnchor()) { |
| 786 | if (DEBUG) |
| 787 | log.trace("Situation is [problem][anchor]"); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 788 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 789 | // Insert the solution |
| 790 | try { |
| 791 | this.segments.set( |
| 792 | i+1, |
| 793 | _merge(this.segments.get(i+1), underScrutiny, false) |
| 794 | ); |
| 795 | } |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 796 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 797 | // An error occurred while solving the problem |
| 798 | catch (QueryException e) { |
| 799 | return false; |
| 800 | }; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 801 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 802 | // Remove the problem |
| 803 | this.segments.remove(i); |
| 804 | size--; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 805 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 806 | if (DEBUG) |
| 807 | log.trace("Remove segment {} - now size {}", i, size); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 808 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 809 | // Restart checking |
| 810 | i = 0; |
| 811 | } |
| Nils Diewald | b84e727 | 2014-11-07 01:27:38 +0000 | [diff] [blame] | 812 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 813 | // [anchor][problem] |
| 814 | else if (i >= 1 && this.segments.get(i-1).maybeAnchor()) { |
| 815 | if (DEBUG) |
| 816 | log.trace("Situation is [anchor][problem]"); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 817 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 818 | // Insert the solution |
| 819 | try { |
| 820 | this.segments.set( |
| 821 | i-1, |
| 822 | _merge(this.segments.get(i-1), underScrutiny, true) |
| 823 | ); |
| 824 | } |
| 825 | catch (QueryException e) { |
| 826 | return false; |
| 827 | }; |
| Nils Diewald | b84e727 | 2014-11-07 01:27:38 +0000 | [diff] [blame] | 828 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 829 | // Remove the problem |
| 830 | this.segments.remove(i); |
| 831 | size--; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 832 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 833 | if (DEBUG) |
| 834 | log.trace("Remove segment {} - now size {}", i, size); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 835 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 836 | // Restart checking |
| 837 | i = 0; |
| 838 | } |
| 839 | // [problem][problem] |
| 840 | else { |
| 841 | if (DEBUG) |
| 842 | log.trace("Situation is [problem][problem]"); |
| 843 | noRemainingProblem = false; |
| 844 | i++; |
| 845 | }; |
| 846 | } |
| 847 | else { |
| 848 | if (DEBUG) |
| 849 | log.trace("segment {} can be an anchor", i); |
| 850 | i++; |
| 851 | }; |
| 852 | }; |
| Nils Diewald | b84e727 | 2014-11-07 01:27:38 +0000 | [diff] [blame] | 853 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 854 | // There is still a remaining problem |
| 855 | if (!noRemainingProblem) { |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 856 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 857 | // The size has changed - retry! |
| 858 | if (size != this.segments.size()) |
| 859 | return _solveProblematicSequence(); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 860 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 861 | this.isSolved = true; |
| 862 | return true; |
| 863 | }; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 864 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 865 | this.isSolved = true; |
| 866 | return false; |
| Nils Diewald | d7cb0eb | 2014-02-12 23:06:10 +0000 | [diff] [blame] | 867 | }; |
| 868 | |
| Nils Diewald | 164f8be | 2014-02-13 02:43:16 +0000 | [diff] [blame] | 869 | |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 870 | // Todo: Deal with negative and optional! |
| 871 | // [base=der][base!=Baum]? |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 872 | private SpanQueryWrapper _merge (SpanQueryWrapper anchor, |
| 873 | SpanQueryWrapper problem, |
| 874 | boolean mergeLeft) throws QueryException { |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 875 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 876 | // Extend to the right - merge to the left |
| 877 | int direction = mergeLeft ? 1 : -1; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 878 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 879 | if (DEBUG) |
| 880 | log.trace("Will merge two spans to {}", |
| 881 | mergeLeft ? "left" : "right"); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 882 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 883 | // Make empty extension to anchor |
| 884 | if (problem.isEmpty()) { |
| 885 | SpanQuery query; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 886 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 887 | if (DEBUG) |
| 888 | log.trace("Problem is empty with class {}", |
| 889 | problem.getClassNumber()); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 890 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 891 | query = new SpanExpansionQuery( |
| Nils Diewald | b9dd413 | 2015-02-16 16:32:41 +0000 | [diff] [blame^] | 892 | anchor.retrieveNode(this.retrieveNode).toQuery(), |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 893 | problem.getMin(), |
| 894 | problem.getMax(), |
| 895 | direction, |
| 896 | problem.hasClass() ? problem.getClassNumber() : (byte) 0, |
| 897 | true |
| 898 | ); |
| 899 | return new SpanSimpleQueryWrapper(query).isExtended(true); |
| 900 | } |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 901 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 902 | // make negative extension to anchor |
| 903 | else if (problem.isNegative()) { |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 904 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 905 | SpanQuery query; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 906 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 907 | if (DEBUG) |
| 908 | log.trace("Problem is negative with class {}", |
| 909 | problem.getClassNumber()); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 910 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 911 | query = new SpanExpansionQuery( |
| Nils Diewald | b9dd413 | 2015-02-16 16:32:41 +0000 | [diff] [blame^] | 912 | anchor.retrieveNode(this.retrieveNode).toQuery(), |
| 913 | problem.retrieveNode(this.retrieveNode).toQuery(), |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 914 | problem.getMin(), |
| 915 | problem.getMax(), |
| 916 | direction, |
| 917 | problem.hasClass() ? problem.getClassNumber() : (byte) 0, |
| 918 | true |
| 919 | ); |
| 920 | return new SpanSimpleQueryWrapper(query).isExtended(true); |
| 921 | }; |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 922 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 923 | if (DEBUG) |
| 924 | log.trace("Problem is optional"); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 925 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 926 | // [base=der][base=baum]? |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 927 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 928 | // [base=der] |
| 929 | SpanAlterQueryWrapper saqw = |
| 930 | new SpanAlterQueryWrapper(this.field, anchor); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 931 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 932 | // [base=der] |
| 933 | SpanSequenceQueryWrapper ssqw = |
| 934 | new SpanSequenceQueryWrapper(this.field, anchor); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 935 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 936 | // [base=der][base=baum] |
| 937 | if (mergeLeft) |
| Nils Diewald | b9dd413 | 2015-02-16 16:32:41 +0000 | [diff] [blame^] | 938 | ssqw.append(new SpanSimpleQueryWrapper(problem.retrieveNode(this.retrieveNode).toQuery())); |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 939 | // [base=baum][base=der] |
| 940 | else |
| Nils Diewald | b9dd413 | 2015-02-16 16:32:41 +0000 | [diff] [blame^] | 941 | ssqw.prepend(new SpanSimpleQueryWrapper(problem.retrieveNode(this.retrieveNode).toQuery())); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 942 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 943 | saqw.or(ssqw); |
| Nils Diewald | be5943e | 2014-10-21 19:35:34 +0000 | [diff] [blame] | 944 | |
| Nils Diewald | 0cc4f2e | 2015-01-30 20:13:08 +0000 | [diff] [blame] | 945 | return (SpanQueryWrapper) saqw; |
| Nils Diewald | 8db8f92 | 2014-10-24 17:43:13 +0000 | [diff] [blame] | 946 | }; |
| Nils Diewald | f399a67 | 2013-11-18 17:55:22 +0000 | [diff] [blame] | 947 | }; |