| Joachim Bingel | c8a28e4 | 2014-04-24 15:06:42 +0000 | [diff] [blame] | 1 | package de.ids_mannheim.korap.query.serialize; |
| 2 | |
| 3 | import java.lang.reflect.Method; |
| 4 | import java.util.ArrayList; |
| 5 | import java.util.HashMap; |
| 6 | import java.util.LinkedHashMap; |
| 7 | import java.util.LinkedList; |
| 8 | import java.util.List; |
| 9 | import java.util.Map; |
| 10 | import java.util.regex.Matcher; |
| 11 | import java.util.regex.Pattern; |
| 12 | |
| 13 | import org.antlr.v4.runtime.ANTLRInputStream; |
| 14 | import org.antlr.v4.runtime.BailErrorStrategy; |
| 15 | import org.antlr.v4.runtime.CharStream; |
| 16 | import org.antlr.v4.runtime.CommonTokenStream; |
| 17 | import org.antlr.v4.runtime.Lexer; |
| 18 | import org.antlr.v4.runtime.Parser; |
| 19 | import org.antlr.v4.runtime.ParserRuleContext; |
| 20 | import org.antlr.v4.runtime.tree.ParseTree; |
| 21 | |
| 22 | import de.ids_mannheim.korap.query.annis.AqlLexer; |
| 23 | import de.ids_mannheim.korap.query.annis.AqlParser; |
| 24 | import de.ids_mannheim.korap.query.serialize.AbstractSyntaxTree; |
| 25 | import de.ids_mannheim.korap.util.QueryException; |
| 26 | |
| 27 | /** |
| 28 | * Map representation of syntax tree as returned by ANTLR |
| 29 | * @author joachim |
| 30 | * |
| 31 | */ |
| 32 | public class TreeTemplate extends Antlr4AbstractSyntaxTree { |
| 33 | /** |
| 34 | * Top-level map representing the whole request. |
| 35 | */ |
| 36 | LinkedHashMap<String,Object> requestMap = new LinkedHashMap<String,Object>(); |
| 37 | /** |
| 38 | * Keeps track of open node categories |
| 39 | */ |
| 40 | LinkedList<String> openNodeCats = new LinkedList<String>(); |
| 41 | /** |
| 42 | * Flag that indicates whether token fields or meta fields are currently being processed |
| 43 | */ |
| 44 | boolean inMeta = false; |
| 45 | /** |
| 46 | * Parser object deriving the ANTLR parse tree. |
| 47 | */ |
| 48 | static Parser qlParser; |
| 49 | /** |
| 50 | * Keeps track of all visited nodes in a tree |
| 51 | */ |
| 52 | List<ParseTree> visited = new ArrayList<ParseTree>(); |
| 53 | /** |
| 54 | * Keeps track of active object. |
| 55 | */ |
| 56 | LinkedList<LinkedHashMap<String,Object>> objectStack = new LinkedList<LinkedHashMap<String,Object>>(); |
| 57 | /** |
| 58 | * Marks the currently active token in order to know where to add flags (might already have been taken away from token stack). |
| 59 | */ |
| 60 | LinkedHashMap<String,Object> curToken = new LinkedHashMap<String,Object>(); |
| 61 | |
| 62 | private LinkedList<ArrayList<ArrayList<Object>>> distributedOperandsLists = new LinkedList<ArrayList<ArrayList<Object>>>(); |
| 63 | |
| 64 | /** |
| 65 | * Keeps track of how many objects there are to pop after every recursion of {@link #processNode(ParseTree)} |
| 66 | */ |
| 67 | LinkedList<Integer> objectsToPop = new LinkedList<Integer>(); |
| 68 | Integer stackedObjects = 0; |
| 69 | public static boolean verbose = false; |
| 70 | |
| 71 | /** |
| 72 | * |
| 73 | * @param tree The syntax tree as returned by ANTLR |
| 74 | * @param parser The ANTLR parser instance that generated the parse tree |
| 75 | */ |
| 76 | public TreeTemplate(String query) { |
| 77 | // prepareContext(); |
| 78 | requestMap.put("@context", "http://ids-mannheim.de/ns/KorAP/json-ld/v0.1/context.jsonld"); |
| 79 | try { |
| 80 | process(query); |
| 81 | } catch (QueryException e) { |
| 82 | e.printStackTrace(); |
| 83 | } |
| 84 | System.out.println(">>> "+requestMap.get("query")+" <<<"); |
| 85 | } |
| 86 | |
| 87 | private void prepareContext() { |
| 88 | LinkedHashMap<String,Object> context = new LinkedHashMap<String,Object>(); |
| 89 | LinkedHashMap<String,Object> operands = new LinkedHashMap<String,Object>(); |
| 90 | LinkedHashMap<String,Object> relation = new LinkedHashMap<String,Object>(); |
| 91 | LinkedHashMap<String,Object> classMap = new LinkedHashMap<String,Object>(); |
| 92 | |
| 93 | operands.put("@id", "korap:operands"); |
| 94 | operands.put("@container", "@list"); |
| 95 | |
| 96 | relation.put("@id", "korap:relation"); |
| 97 | relation.put("@type", "korap:relation#types"); |
| 98 | |
| 99 | classMap.put("@id", "korap:class"); |
| 100 | classMap.put("@type", "xsd:integer"); |
| 101 | |
| 102 | context.put("korap", "http://korap.ids-mannheim.de/ns/query"); |
| 103 | context.put("@language", "de"); |
| 104 | context.put("operands", operands); |
| 105 | context.put("relation", relation); |
| 106 | context.put("class", classMap); |
| 107 | context.put("query", "korap:query"); |
| 108 | context.put("filter", "korap:filter"); |
| 109 | context.put("meta", "korap:meta"); |
| 110 | |
| 111 | requestMap.put("@context", context); |
| 112 | } |
| 113 | |
| 114 | @Override |
| 115 | public Map<String, Object> getRequestMap() { |
| 116 | return requestMap; |
| 117 | } |
| 118 | |
| 119 | @Override |
| 120 | public void process(String query) throws QueryException { |
| 121 | ParseTree tree = parseAnnisQuery(query); |
| 122 | System.out.println("Processing Annis QL"); |
| 123 | processNode(tree); |
| 124 | } |
| 125 | |
| 126 | private void processNode(ParseTree node) { |
| 127 | // Top-down processing |
| 128 | if (visited.contains(node)) return; |
| 129 | else visited.add(node); |
| 130 | |
| 131 | String nodeCat = getNodeCat(node); |
| 132 | openNodeCats.push(nodeCat); |
| 133 | |
| 134 | stackedObjects = 0; |
| 135 | |
| 136 | if (verbose) { |
| 137 | System.err.println(" "+objectStack); |
| 138 | System.out.println(openNodeCats); |
| 139 | } |
| 140 | |
| 141 | /* |
| 142 | **************************************************************** |
| 143 | **************************************************************** |
| 144 | * Processing individual node categories * |
| 145 | **************************************************************** |
| 146 | **************************************************************** |
| 147 | */ |
| 148 | |
| 149 | objectsToPop.push(stackedObjects); |
| 150 | |
| 151 | /* |
| 152 | **************************************************************** |
| 153 | **************************************************************** |
| 154 | * recursion until 'request' node (root of tree) is processed * |
| 155 | **************************************************************** |
| 156 | **************************************************************** |
| 157 | */ |
| 158 | for (int i=0; i<node.getChildCount(); i++) { |
| 159 | ParseTree child = node.getChild(i); |
| 160 | processNode(child); |
| 161 | } |
| 162 | |
| 163 | |
| 164 | /* |
| 165 | ************************************************************** |
| 166 | * Stuff that happens after processing the children of a node * |
| 167 | ************************************************************** |
| 168 | */ |
| 169 | |
| 170 | |
| 171 | for (int i=0; i<objectsToPop.pop(); i++) { |
| 172 | objectStack.pop(); |
| 173 | } |
| 174 | |
| 175 | |
| 176 | openNodeCats.pop(); |
| 177 | |
| 178 | } |
| 179 | |
| Joachim Bingel | c8a28e4 | 2014-04-24 15:06:42 +0000 | [diff] [blame] | 180 | @SuppressWarnings("unused") |
| 181 | private void putIntoSuperObject(LinkedHashMap<String, Object> object) { |
| 182 | putIntoSuperObject(object, 0); |
| 183 | } |
| 184 | |
| 185 | @SuppressWarnings({ "unchecked" }) |
| 186 | private void putIntoSuperObject(LinkedHashMap<String, Object> object, int objStackPosition) { |
| 187 | if (distributedOperandsLists.size()>0) { |
| 188 | ArrayList<ArrayList<Object>> distributedOperands = distributedOperandsLists.pop(); |
| 189 | for (ArrayList<Object> operands : distributedOperands) { |
| 190 | operands.add(object); |
| 191 | } |
| 192 | } else if (objectStack.size()>objStackPosition) { |
| 193 | ArrayList<Object> topObjectOperands = (ArrayList<Object>) objectStack.get(objStackPosition).get("operands"); |
| 194 | topObjectOperands.add(0, object); |
| 195 | |
| 196 | } else { |
| 197 | requestMap.put("query", object); |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | private static ParserRuleContext parseAnnisQuery (String p) throws QueryException { |
| 202 | Lexer poliqarpLexer = new AqlLexer((CharStream)null); |
| 203 | ParserRuleContext tree = null; |
| 204 | // Like p. 111 |
| 205 | try { |
| 206 | |
| 207 | // Tokenize input data |
| 208 | ANTLRInputStream input = new ANTLRInputStream(p); |
| 209 | poliqarpLexer.setInputStream(input); |
| 210 | CommonTokenStream tokens = new CommonTokenStream(poliqarpLexer); |
| 211 | qlParser = new AqlParser(tokens); |
| 212 | |
| 213 | // Don't throw out erroneous stuff |
| 214 | qlParser.setErrorHandler(new BailErrorStrategy()); |
| 215 | qlParser.removeErrorListeners(); |
| 216 | |
| 217 | // Get starting rule from parser |
| 218 | Method startRule = AqlParser.class.getMethod("start"); |
| 219 | tree = (ParserRuleContext) startRule.invoke(qlParser, (Object[])null); |
| 220 | } |
| 221 | |
| 222 | // Some things went wrong ... |
| 223 | catch (Exception e) { |
| 224 | System.err.println( e.getMessage() ); |
| 225 | } |
| 226 | |
| 227 | if (tree == null) { |
| 228 | throw new QueryException("Could not parse query. Make sure it is correct QL syntax."); |
| 229 | } |
| 230 | |
| 231 | // Return the generated tree |
| 232 | return tree; |
| 233 | } |
| 234 | |
| 235 | public static void main(String[] args) { |
| 236 | /* |
| 237 | * For testing |
| 238 | */ |
| 239 | String[] queries = new String[] { |
| 240 | }; |
| 241 | TreeTemplate.verbose=true; |
| 242 | for (String q : queries) { |
| 243 | try { |
| 244 | System.out.println(q); |
| 245 | System.out.println(TreeTemplate.parseAnnisQuery(q).toStringTree(TreeTemplate.qlParser)); |
| 246 | @SuppressWarnings("unused") |
| 247 | TreeTemplate at = new TreeTemplate(q); |
| 248 | // System.out.println(TreeTemplate.parseAnnisQuery(q).toStringTree(TreeTemplate.aqlParser)); |
| 249 | System.out.println(); |
| 250 | |
| 251 | } catch (NullPointerException | QueryException npe) { |
| 252 | npe.printStackTrace(); |
| 253 | } |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | } |