/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * *************************************************************************************** */ package com.espertech.esper.epl.parse; import com.espertech.esper.collection.Pair; import com.espertech.esper.epl.generated.EsperEPL2GrammarParser; import org.antlr.v4.runtime.CommonTokenStream; import java.io.ByteArrayOutputStream; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * Walker to annotation stuctures. */ public class ASTJsonHelper { public static Object walk(CommonTokenStream tokenStream, EsperEPL2GrammarParser.JsonvalueContext node) throws ASTWalkException { if (node.constant() != null) { EsperEPL2GrammarParser.ConstantContext constCtx = node.constant(); if (constCtx.stringconstant() != null) { return extractString(constCtx.stringconstant().getText()); } else { return ASTConstantHelper.parse(constCtx.getChild(0)); } } else if (node.jsonobject() != null) { return walkObject(tokenStream, node.jsonobject()); } else if (node.jsonarray() != null) { return walkArray(tokenStream, node.jsonarray()); } throw ASTWalkException.from("Encountered unexpected node type in json tree", tokenStream, node); } public static Map<String, Object> walkObject(CommonTokenStream tokenStream, EsperEPL2GrammarParser.JsonobjectContext ctx) { Map<String, Object> map = new LinkedHashMap<String, Object>(); List<EsperEPL2GrammarParser.JsonpairContext> pairs = ctx.jsonmembers().jsonpair(); for (EsperEPL2GrammarParser.JsonpairContext pair : pairs) { Pair<String, Object> value = walkJSONField(tokenStream, pair); map.put(value.getFirst(), value.getSecond()); } return map; } public static List<Object> walkArray(CommonTokenStream tokenStream, EsperEPL2GrammarParser.JsonarrayContext ctx) { List<Object> list = new ArrayList<Object>(); if (ctx.jsonelements() == null) { return list; } List<EsperEPL2GrammarParser.JsonvalueContext> values = ctx.jsonelements().jsonvalue(); for (EsperEPL2GrammarParser.JsonvalueContext value : values) { Object val = walk(tokenStream, value); list.add(val); } return list; } private static Pair<String, Object> walkJSONField(CommonTokenStream tokenStream, EsperEPL2GrammarParser.JsonpairContext ctx) { String label; if (ctx.stringconstant() != null) { label = extractString(ctx.stringconstant().getText()); } else { label = ctx.keywordAllowedIdent().getText(); } Object value = walk(tokenStream, ctx.jsonvalue()); return new Pair<String, Object>(label, value); } private static String extractString(String text) { StringBuffer sb = new StringBuffer(text); int startPoint = 1; for (;;) { int slashIndex = sb.indexOf("\\", startPoint); if (slashIndex == -1) { break; } char escapeType = sb.charAt(slashIndex + 1); switch (escapeType) { case 'u': String unicode = extractUnicode(sb, slashIndex); sb.replace(slashIndex, slashIndex + 6, unicode); // backspace break; // back to the loop // note: Java's character escapes match JSON's, which is why it looks like we're replacing // "\b" with "\b". We're actually replacing 2 characters (slash-b) with one (backspace). case 'b': sb.replace(slashIndex, slashIndex + 2, "\b"); break; case 't': sb.replace(slashIndex, slashIndex + 2, "\t"); break; case 'n': sb.replace(slashIndex, slashIndex + 2, "\n"); break; case 'f': sb.replace(slashIndex, slashIndex + 2, "\f"); break; case 'r': sb.replace(slashIndex, slashIndex + 2, "\r"); break; case '\'': sb.replace(slashIndex, slashIndex + 2, "\'"); break; case '\"': sb.replace(slashIndex, slashIndex + 2, "\""); break; case '\\': sb.replace(slashIndex, slashIndex + 2, "\\"); break; case '/': sb.replace(slashIndex, slashIndex + 2, "/"); break; default: break; } startPoint = slashIndex + 1; } sb.deleteCharAt(0); sb.deleteCharAt(sb.length() - 1); return sb.toString(); } private static String extractUnicode(StringBuffer sb, int slashIndex) { String result; String code = sb.substring(slashIndex + 2, slashIndex + 6); int charNum = Integer.parseInt(code, 16); // hex to integer try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(baos, "UTF-8"); osw.write(charNum); osw.flush(); result = baos.toString("UTF-8"); // Thanks to Silvester Pozarnik for the tip about adding "UTF-8" here } catch (Exception e) { throw ASTWalkException.from("Failed to obtain for unicode '" + charNum + "'", e); } return result; } }