/************************************************************************ * Copyright (c) 2015 IoT-Solutions e.U. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ************************************************************************/ package iot.jcypher.domainquery.internal; import java.lang.reflect.Array; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Map; import iot.jcypher.domainquery.internal.RecordedQuery.Assignment; import iot.jcypher.domainquery.internal.RecordedQuery.DOMatchRef; import iot.jcypher.domainquery.internal.RecordedQuery.Invocation; import iot.jcypher.domainquery.internal.RecordedQuery.Literal; import iot.jcypher.domainquery.internal.RecordedQuery.Reference; import iot.jcypher.domainquery.internal.RecordedQuery.Statement; import iot.jcypher.query.values.JcValue; import iot.jcypher.query.values.ValueAccess; public class RecordedQueryToString { private static SimpleDateFormat dateFormat = new SimpleDateFormat( "dd.MM.yyyy HH:mm:ss.SSS"); public static String queryToString(RecordedQuery query) { Context context = new Context(); context.augmentations = query.getAugmentations(); context.sb.append(query.isGeneric() ? "Generic-DomainQuery" : "DomainQuery"); context.sb.append("\n"); List<Statement> stmts = query.getStatements(); statementsToString(stmts, context); return context.sb.toString(); } private static void statementsToString(List<Statement> statements, Context context) { Statement prev = null; for(int i = 0; i < statements.size(); i++) { Statement s = statements.get(i); context.indent.calcBefore(s, context.callDepth); boolean separator = false; if (prev instanceof Invocation && !(prev instanceof Assignment) && s instanceof Invocation) { if (((Invocation)prev).getReturnObjectRef().equals(((Invocation)s).getOnObjectRef())) { context.sb.append('.'); separator = true; } } if (!separator) { // start new statement if (prev != null) { if (context.callDepth > 0) { // if s is a stacked statement on the domain query if (s instanceof Invocation && ((Invocation)s).getOnObjectRef().equals(QueryRecorder.QUERY_ID)) { context.sb.append(",\n"); context.sb.append(context.indent.getIndent()); } else context.sb.append(", "); } else { context.sb.append(";\n"); context.sb.append(context.indent.getIndent()); } } else if (context.callDepth > 0) { // start of stacked statements if (s instanceof Invocation && ((Invocation)s).getOnObjectRef().equals(QueryRecorder.QUERY_ID)) { context.sb.append('\n'); context.sb.append(context.indent.getIndent()); } } if (context.callDepth == 0) context.topStatementStart = context.sb.length(); // needed for assignment statements statementToString(s, context); } else // concatenate statements callToString((Invocation)s, context); // must be an Invocation context.indent.calcAfter(s, context.callDepth); if (context.callDepth == 0 && i == statements.size() - 1) // the last one context.sb.append(';'); prev = s; } } private static void statementToString(Statement statement, Context context) { if (statement instanceof Literal) { if (((Literal)statement).getValue() != null) PrimitiveWriter.writePrimitiveValue(((Literal)statement).getValue(), context.sb); else context.sb.append("null"); } else if (statement instanceof Invocation) { invocationToString((Invocation)statement, context); } else if (statement instanceof DOMatchRef) { context.sb.append(context.getAugmented(((DOMatchRef)statement).getRef())); } else if (statement instanceof Reference) { context.sb.append(((Reference)statement).getRefId()); } } private static void invocationToString(RecordedQuery.Invocation invocation, Context context) { context.sb.append(context.getAugmented(invocation.getOnObjectRef())); context.sb.append('.'); callToString(invocation, context); } private static void callToString(RecordedQuery.Invocation invocation, Context context) { context.sb.append(invocation.getMethod()); context.sb.append('('); List<Statement> params = invocation.getParams(); context.callDepth++; context.indent.increment(); if (params != null) statementsToString(params, context); context.indent.decrement(); context.callDepth--; context.sb.append(')'); if (invocation instanceof Assignment) { context.sb.insert(context.topStatementStart, " = "); context.sb.insert(context.topStatementStart, context.getAugmented(invocation.getReturnObjectRef())); } } /******************************************/ private static class Indent { private static final String BR_OPEN = "BR_OPEN"; private static final String BR_CLOSE = "BR_CLOSE"; private static final String IN = " "; private int level = 0; private String indent = new String(); private String getIndent() { return indent; } private void calcBefore(Statement statement, int callDepth) { String hint = statement.getHint(); if (BR_CLOSE.equals(hint)) { if (level > 0) { level--; buildIndent(); } } } private void calcAfter(Statement statement, int callDepth) { String hint = statement.getHint(); if (BR_OPEN.equals(hint)) { level++; buildIndent(); } } private void increment() { level++; buildIndent(); } private void decrement() { if (level > 0) { level--; buildIndent(); } } private void buildIndent() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < level; i++) { sb.append(IN); } indent = sb.toString(); } } /******************************************/ private static class Context { private Indent indent = new Indent(); private int callDepth = 0; private int topStatementStart; private Map<String, String> augmentations; private StringBuilder sb = new StringBuilder(); private String getAugmented(String str) { String aug; if (this.augmentations != null && (aug = this.augmentations.get(str)) != null) return aug; return str; } } /****************************************/ private static class PrimitiveWriter { private static void writePrimitiveValue(Object val, StringBuilder sb) { if (val instanceof Number) { sb.append(val.toString()); } else if (val instanceof Boolean) { sb.append(val.toString()); } else if (val instanceof List<?>) { List<?> list = (List<?>)val; for (int i = 0; i < list.size(); i++) { if (i > 0) sb.append(", "); writePrimitiveValue(list.get(i), sb); } } else if (val.getClass().isArray()) { int len = Array.getLength(val); for (int i = 0; i < len; i++) { if (i > 0) sb.append(", "); writePrimitiveValue(Array.get(val, i), sb); } } else if (val instanceof JcValue) { sb.append(ValueAccess.getName((JcValue)val)); } else if (val instanceof Date) { sb.append(dateFormat.format((Date)val)); } else { sb.append('\''); sb.append(val.toString()); sb.append('\''); } } } }