| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 502acaa commit 16bcd7a
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,6 +13,7 @@ | |||
| 13 | 13 | import java.io.IOException; | |
| 14 | 14 | import java.util.ArrayList; | |
| 15 | 15 | import java.util.HashMap; | |
| 16 | + import java.util.Iterator; | ||
| 16 | 17 | import java.util.Map; | |
| 17 | 18 | import java.util.regex.Pattern; | |
| 18 | 19 | ||
@@ -22,6 +23,8 @@ | |||
| 22 | 23 | * Converts MathML queries into XQueries. | |
| 23 | 24 | * The result is then wrapped around with a header and a footer (defaults to a DB2 header/footer if not given). | |
| 24 | 25 | * The variable $x always represents a hit, so you can refer to $x in the footer as the result node. | |
| 26 | + * The variable $q always represents a map of qvars to their respective formula ID, so you can refer to $q in the footer | ||
| 27 | + * to return qvar results. | ||
| 25 | 28 | * Created by Moritz Schubotz on 9/3/14. | |
| 26 | 29 | * Translated from http://git.wikimedia.org/blob/mediawiki%2Fextensions%2FMathSearch.git/31a80ae48d1aaa50da9103cea2e45a8dc2204b39/XQueryGenerator.php | |
| 27 | 30 | */ | |
@@ -31,7 +34,10 @@ public class XQueryGenerator { | |||
| 31 | 34 | //Qvar map of qvar name to XPaths referenced by each qvar | |
| 32 | 35 | private Map<String, ArrayList<String>> qvar = new HashMap<>(); | |
| 33 | 36 | private String relativeXPath = ""; | |
| 37 | + private String exactMatchXQuery = ""; | ||
| 34 | 38 | private String lengthConstraint = ""; | |
| 39 | + private String qvarConstraint = ""; | ||
| 40 | + private String qvarMapVariable = ""; | ||
| 35 | 41 | private String header = "declare default element namespace \"http://www.w3.org/1998/Math/MathML\";\n" + | |
| 36 | 42 | "for $m in db2-fn:xmlcolumn(\"math.math_mathml\") return\n"; | |
| 37 | 43 | private String footer = "data($m/*[1]/@alttext)"; | |
@@ -134,80 +140,101 @@ public void setMainElement( Node mainElement ) { | |||
| 134 | 140 | ||
| 135 | 141 | /** | |
| 136 | 142 | * Generates the constraints of the XQuery and then builds the XQuery and returns it as a string | |
| 137 | - * @return XQuery as string | ||
| 143 | + * @return XQuery as string. Returns null if no main element set. | ||
| 138 | 144 | */ | |
| 139 | 145 | public String toString() { | |
| 140 | 146 | if ( mainElement == null ) { | |
| 141 | 147 | return null; | |
| 142 | 148 | } | |
| 143 | - final String fixedConstraints = generateConstraints( mainElement, true ); | ||
| 144 | - final StringBuilder qvarConstraintString = new StringBuilder(); | ||
| 145 | - //Generate qvar constraint string | ||
| 146 | - //This specifies that the same qvars must refer to the same nodes, using the XQuery "=" equality | ||
| 147 | - //This is equality based on: same text, same node names, and same children by the "=" equality | ||
| 148 | - for ( Map.Entry<String, ArrayList<String>> entry : qvar.entrySet() ) { | ||
| 149 | - final StringBuilder addString = new StringBuilder(); | ||
| 150 | - if ( entry.getValue().size() > 1 ) { | ||
| 151 | - final String firstEntry = entry.getValue().get( 0 ); | ||
| 152 | - if ( qvarConstraintString.length() != 0 ) { | ||
| 153 | - addString.append("\n and "); | ||
| 154 | - } | ||
| 155 | - String lastEntry = ""; | ||
| 156 | - boolean newContent = false; | ||
| 157 | - //begins at second entry | ||
| 158 | - for ( final String currentEntry : entry.getValue() ) { | ||
| 159 | - if ( !currentEntry.equals( firstEntry ) ) { | ||
| 160 | - if ( !lastEntry.isEmpty() ) { | ||
| 161 | - addString.append(" and "); | ||
| 162 | - } | ||
| 163 | - addString.append("$x").append(firstEntry).append(" = $x").append(currentEntry); | ||
| 164 | - lastEntry = currentEntry; | ||
| 165 | - newContent = true; | ||
| 166 | - } | ||
| 167 | - } | ||
| 168 | - if ( newContent ) { | ||
| 169 | - qvarConstraintString.append(addString); | ||
| 170 | - } | ||
| 171 | - } | ||
| 172 | - } | ||
| 173 | - return getString( mainElement, fixedConstraints, qvarConstraintString.toString() ); | ||
| 149 | + exactMatchXQuery = generateSimpleConstraints( mainElement, true ); | ||
| 150 | + generateQvarConstraints(); | ||
| 151 | + | ||
| 152 | + return getString( mainElement, exactMatchXQuery, lengthConstraint, qvarConstraint, qvarMapVariable, header, footer ); | ||
| 174 | 153 | } | |
| 175 | 154 | ||
| 176 | 155 | /** | |
| 177 | - * Builds the XQuery as a string with the set header and footer, given constraint strings and the main element. | ||
| 156 | + * Builds the XQuery as a string given constraint strings, header, footer, qvar map, and the main element. | ||
| 178 | 157 | * @param mainElement Node from which to build XQuery | |
| 179 | 158 | * @param fixedConstraints Constraint string for basic exact formula matching | |
| 159 | + * @param lengthConstraint Constraint string for length of variables | ||
| 180 | 160 | * @param qvarConstraintString Constraint string for qvar matching | |
| 161 | + * @param qvarMapVariable Qvar map variable | ||
| 162 | + * @param header Header | ||
| 163 | + * @param footer Footer | ||
| 181 | 164 | * @return XQuery as string | |
| 182 | 165 | */ | |
| 183 | - public String getString( Node mainElement, String fixedConstraints, String qvarConstraintString ) { | ||
| 166 | + public static String getString( Node mainElement, String fixedConstraints, String lengthConstraint, | ||
| 167 | + String qvarConstraintString, String qvarMapVariable, String header, String footer ) { | ||
| 184 | 168 | String out = header; | |
| 185 | 169 | out += "for $x in $m//*:" + getFirstChild( mainElement ).getLocalName() + "\n" + | |
| 186 | 170 | fixedConstraints + "\n"; | |
| 187 | - out += getConstraints( qvarConstraintString ); | ||
| 188 | - out += | ||
| 189 | - "return" + "\n" + getFooter(); | ||
| 171 | + if ( !lengthConstraint.isEmpty() || !qvarConstraintString.isEmpty() ) { | ||
| 172 | + out += "where" + "\n"; | ||
| 173 | + if ( lengthConstraint.isEmpty() ) { | ||
| 174 | + out += qvarConstraintString; | ||
| 175 | + } else { | ||
| 176 | + out += lengthConstraint + (qvarConstraintString.isEmpty() ? "" : " and " + qvarConstraintString); | ||
| 177 | + } | ||
| 178 | + } | ||
| 179 | + out += qvarMapVariable; | ||
| 180 | + out += "\n"; | ||
| 181 | + out += "return" + "\n" + footer; | ||
| 190 | 182 | return out; | |
| 191 | 183 | } | |
| 192 | 184 | ||
| 193 | 185 | /** | |
| 194 | - * Appends constraint strings together | ||
| 195 | - * @param qvarConstraintString Qvar constraint portion of query | ||
| 196 | - * @return All constraint strings appended together | ||
| 186 | + * Uses the qvar map to generate a XQuery string containing qvar constraints, | ||
| 187 | + * and the qvar map variable which maps qvar names to their respective formula ID's in the result. | ||
| 197 | 188 | */ | |
| 198 | - private String getConstraints( String qvarConstraintString ) { | ||
| 199 | - String out = lengthConstraint + | ||
| 200 | - (((qvarConstraintString.length() > 0) && (lengthConstraint.length() > 0)) ? " and " : "") + | ||
| 201 | - qvarConstraintString; | ||
| 202 | - if ( out.trim().length() > 0 ) { | ||
| 203 | - return "where" + "\n" + out + "\n"; | ||
| 204 | - } else { | ||
| 205 | - return ""; | ||
| 189 | + private void generateQvarConstraints() { | ||
| 190 | + final StringBuilder qvarConstrBuilder = new StringBuilder(); | ||
| 191 | + final StringBuilder qvarMapStrBuilder = new StringBuilder(); | ||
| 192 | + final Iterator<Map.Entry<String, ArrayList<String>>> entryIterator = qvar.entrySet().iterator(); | ||
| 193 | + if ( entryIterator.hasNext() ) { | ||
| 194 | + qvarMapStrBuilder.append( "\nlet $q := map {" ); | ||
| 195 | + | ||
| 196 | + while ( entryIterator.hasNext() ) { | ||
| 197 | + final Map.Entry<String, ArrayList<String>> currentEntry = entryIterator.next(); | ||
| 198 | + | ||
| 199 | + final Iterator<String> valueIterator = currentEntry.getValue().iterator(); | ||
| 200 | + final String firstValue = valueIterator.next(); | ||
| 201 | + | ||
| 202 | + qvarMapStrBuilder.append( '"' ).append( currentEntry.getKey() ).append( '"' ) | ||
| 203 | + .append( " : (data($x" ).append( firstValue ).append( "/@xml:id)" ); | ||
| 204 | + | ||
| 205 | + //check if there are additional values that we need to constrain | ||
| 206 | + if ( valueIterator.hasNext() ) { | ||
| 207 | + if ( qvarConstrBuilder.length() > 0 ) { | ||
| 208 | + //only add beginning and if it's an additional constraint in the aggregate qvar string | ||
| 209 | + qvarConstrBuilder.append( "\n and " ); | ||
| 210 | + } | ||
| 211 | + while ( valueIterator.hasNext() ) { | ||
| 212 | + //process second value onwards | ||
| 213 | + final String currentValue = valueIterator.next(); | ||
| 214 | + qvarMapStrBuilder.append( ",data($x" ).append( currentValue ).append( "/@xml-id)" ); | ||
| 215 | + //These constraints specify that the same qvars must refer to the same nodes, | ||
| 216 | + //using the XQuery "=" equality | ||
| 217 | + //This is equality based on: same text, same node names, and same children nodes | ||
| 218 | + qvarConstrBuilder.append( "$x" ).append( firstValue ).append( " = $x" ).append( currentValue ); | ||
| 219 | + if ( valueIterator.hasNext() ) { | ||
| 220 | + qvarConstrBuilder.append( " and " ); | ||
| 221 | + } | ||
| 222 | + } | ||
| 223 | + } | ||
| 224 | + qvarMapStrBuilder.append( ')' ); | ||
| 225 | + if ( entryIterator.hasNext() ) { | ||
| 226 | + qvarMapStrBuilder.append( ',' ); | ||
| 227 | + } | ||
| 228 | + } | ||
| 229 | + qvarMapStrBuilder.append( '}' ); | ||
| 206 | 230 | } | |
| 231 | + qvarMapVariable = qvarMapStrBuilder.toString(); | ||
| 232 | + qvarConstraint = qvarConstrBuilder.toString(); | ||
| 207 | 233 | } | |
| 208 | 234 | ||
| 209 | - private String generateConstraint( Node node ) { | ||
| 210 | - return generateConstraints( node, false ); | ||
| 235 | + | ||
| 236 | + private String generateSimpleConstraints( Node node ) { | ||
| 237 | + return generateSimpleConstraints( node, false ); | ||
| 211 | 238 | } | |
| 212 | 239 | ||
| 213 | 240 | /** | |
@@ -218,7 +245,7 @@ private String generateConstraint( Node node ) { | |||
| 218 | 245 | * is not added as a constraint here, but in getString()) | |
| 219 | 246 | * @return Exact match XQuery string | |
| 220 | 247 | */ | |
| 221 | - private String generateConstraints( Node node, boolean isRoot ) { | ||
| 248 | + private String generateSimpleConstraints( Node node, boolean isRoot ) { | ||
| 222 | 249 | //Index of child node | |
| 223 | 250 | int childElementIndex = 0; | |
| 224 | 251 | final StringBuilder out = new StringBuilder(); | |
@@ -264,10 +291,10 @@ private String generateConstraints( Node node, boolean isRoot ) { | |||
| 264 | 291 | //Add relative constraint so this can be recursively called | |
| 265 | 292 | out.append( " and *[" ).append( childElementIndex ).append( "]" ); | |
| 266 | 293 | } | |
| 267 | - final String constraint = generateConstraint( child ); | ||
| 294 | + final String constraint = generateSimpleConstraints( child ); | ||
| 268 | 295 | if ( !constraint.isEmpty() ) { | |
| 269 | 296 | //This constraint appears as a child of the relative constraint above (e.g. [*1][constraint]) | |
| 270 | - out.append( "[" ).append( constraint ).append( "]"); | ||
| 297 | + out.append( "[" ).append( constraint ).append( "]" ); | ||
| 271 | 298 | } | |
| 272 | 299 | } | |
| 273 | 300 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -109,6 +109,7 @@ public void testNoRestriction() throws Exception { | |||
| 109 | 109 | "[*[1]/name() = 'plus' and *[2]/name() = 'apply' and *[2][*[1]/name() = 'csymbol' and *[1][./text() = 'superscript'] and *[3]/name() = 'cn' and *[3][./text() = '2']]]\n" + | |
| 110 | 110 | "where\n" + | |
| 111 | 111 | "$x/*[2]/*[2] = $x/*[3]\n" + | |
| 112 | + "let $q := map {\"x\" : (data($x/*[2]/*[2]/@xml:id),data($x/*[3]/@xml-id))}\n" + | ||
| 112 | 113 | "return\n"; | |
| 113 | 114 | Document query = XMLHelper.String2Doc( testInput ); | |
| 114 | 115 | XQueryGenerator xQueryGenerator = new XQueryGenerator( query ); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,5 +7,6 @@ fn:count($x/*[1]/*) = 0 | |||
| 7 | 7 | and fn:count($x/*[3]/*) = 0 | |
| 8 | 8 | and fn:count($x/*) = 3 | |
| 9 | 9 | ||
| 10 | + let $q := map {"x" : (data($x/*[2]/@xml:id))} | ||
| 10 | 11 | return | |
| 11 | 12 | data($m/*[1]/@alttext) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,5 +7,6 @@ fn:count($x/*[1]/*) = 0 | |||
| 7 | 7 | and fn:count($x/*[3]/*) = 0 | |
| 8 | 8 | and fn:count($x/*) = 3 | |
| 9 | 9 | ||
| 10 | + let $q := map {"x" : (data($x/*[2]/@xml:id))} | ||
| 10 | 11 | return | |
| 11 | 12 | data($m/*[1]/@alttext) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,5 +8,6 @@ fn:count($x/*[2]/*[1]/*) = 0 | |||
| 8 | 8 | and fn:count($x/*[2]/*) = 3 | |
| 9 | 9 | and fn:count($x/*) = 3 | |
| 10 | 10 | and $x/*[2]/*[2] = $x/*[3] | |
| 11 | + let $q := map {"x" : (data($x/*[2]/*[2]/@xml:id),data($x/*[3]/@xml-id))} | ||
| 11 | 12 | return | |
| 12 | 13 | data($m/*[1]/@alttext) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,23 @@ | |||
| 1 | + <?xml version="1.0"?> | ||
| 2 | + <mws:query xmlns:mws="http://search.mathweb.org/ns" xmlns:m="http://www.w3.org/1998/Math/MathML" limitmin="0" | ||
| 3 | + answsize="30"> | ||
| 4 | + <mws:expr> | ||
| 5 | + <m:apply xml:id="p1.1.m1.1.5.cmml" xref="p1.1.m1.1.5"> | ||
| 6 | + <m:plus xml:id="p1.3.7.1.4.cmml" xref="p1.765.21.1"/> | ||
| 7 | + <m:apply xml:id="p5.1.3.1.cmml" xref="p4.2.6.21"> | ||
| 8 | + <m:plus xml:id="p1.1.m1.1.3.cmml" xref="p1.1.m1.1.3"/> | ||
| 9 | + <m:apply xml:id="p1.1.m1.1.5.1.cmml" xref="p1.1.m1.1.5.1"> | ||
| 10 | + <m:csymbol cd="ambiguous" xml:id="p1.1.m1.1.5.1.1.cmml">superscript</m:csymbol> | ||
| 11 | + <mws:qvar>x</mws:qvar> | ||
| 12 | + <m:apply xml:id="p1.1.m1.1.5.1.2.cmml" xref="p1.2.4.2.cmml"> | ||
| 13 | + <m:times xml:id="p1.1.m1.1.6.1.3.cmml" xref="p2.31.41.cmml"/> | ||
| 14 | + <mws:qvar>y</mws:qvar> | ||
| 15 | + <mws:qvar>y</mws:qvar> | ||
| 16 | + </m:apply> | ||
| 17 | + </m:apply> | ||
| 18 | + <mws:qvar>x</mws:qvar> | ||
| 19 | + </m:apply> | ||
| 20 | + <mws:qvar>x</mws:qvar> | ||
| 21 | + </m:apply> | ||
| 22 | + </mws:expr> | ||
| 23 | + </mws:query> | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,15 @@ | |||
| 1 | + declare default element namespace "http://www.w3.org/1998/Math/MathML"; | ||
| 2 | + for $m in db2-fn:xmlcolumn("math.math_mathml") return | ||
| 3 | + for $x in $m//*:apply | ||
| 4 | + [*[1]/name() = 'plus' and *[2]/name() = 'apply' and *[2][*[1]/name() = 'plus' and *[2]/name() = 'apply' and *[2][*[1]/name() = 'csymbol' and *[1][./text() = 'superscript'] and *[3]/name() = 'apply' and *[3][*[1]/name() = 'times']]]] | ||
| 5 | + where | ||
| 6 | + fn:count($x/*[2]/*[2]/*[1]/*) = 0 | ||
| 7 | + and fn:count($x/*[2]/*[2]/*[3]/*) = 3 | ||
| 8 | + and fn:count($x/*[2]/*[2]/*) = 3 | ||
| 9 | + and fn:count($x/*[2]/*) = 3 | ||
| 10 | + and fn:count($x/*) = 3 | ||
| 11 | + and $x/*[2]/*[2]/*[2] = $x/*[2]/*[3] and $x/*[2]/*[2]/*[2] = $x/*[3] | ||
| 12 | + and $x/*[2]/*[2]/*[3]/*[2] = $x/*[2]/*[2]/*[3]/*[3] | ||
| 13 | + let $q := map {"x" : (data($x/*[2]/*[2]/*[2]/@xml:id),data($x/*[2]/*[3]/@xml-id),data($x/*[3]/@xml-id)),"y" : (data($x/*[2]/*[2]/*[3]/*[2]/@xml:id),data($x/*[2]/*[2]/*[3]/*[3]/@xml-id))} | ||
| 14 | + return | ||
| 15 | + data($m/*[1]/@alttext) | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments