/* * Copyright (C) 2015 SoftIndex LLC. * * 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 io.datakernel.codegen; import org.objectweb.asm.Label; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; import java.util.LinkedHashMap; import java.util.Map; import static io.datakernel.codegen.Utils.isPrimitiveType; import static io.datakernel.codegen.Utils.wrap; import static org.objectweb.asm.Type.getType; import static org.objectweb.asm.commons.Method.getMethod; /** * Defines methods which allow to create a string */ @SuppressWarnings("PointlessArithmeticExpression") public final class ExpressionToString implements Expression { private String begin = "{"; private String end = "}"; private String separator = " "; private final Map<Object, Expression> arguments = new LinkedHashMap<>(); ExpressionToString(Map<String, Expression> arguments) { this.arguments.putAll(arguments); } ExpressionToString() { } public ExpressionToString withArgument(String label, Expression expression) { this.arguments.put(label, expression); return this; } public ExpressionToString withArgument(Expression expression) { this.arguments.put(arguments.size() + 1, expression); return this; } public ExpressionToString withSeparator(String separator) { this.separator = separator; return this; } public ExpressionToString withQuotes(String begin, String end) { this.begin = begin; this.end = end; return this; } public ExpressionToString withQuotes(String begin, String end, String separator) { this.begin = begin; this.end = end; this.separator = separator; return this; } @Override public Type type(Context ctx) { return getType(String.class); } @Override public Type load(Context ctx) { final GeneratorAdapter g = ctx.getGeneratorAdapter(); g.newInstance(getType(StringBuilder.class)); g.dup(); g.invokeConstructor(getType(StringBuilder.class), getMethod("void <init> ()")); boolean first = true; for (Object key : arguments.keySet()) { String str = first ? begin : separator; first = false; if (key instanceof String) { str += key; } if (!str.isEmpty()) { g.dup(); g.push(str); g.invokeVirtual(getType(StringBuilder.class), getMethod("StringBuilder append(String)")); g.pop(); } g.dup(); final Expression expression = arguments.get(key); final Type type = expression.load(ctx); if (isPrimitiveType(type)) { g.invokeStatic(wrap(type), new Method("toString", getType(String.class), new Type[]{type})); } else { final Label nullLabel = new Label(); final Label afterToString = new Label(); g.dup(); g.ifNull(nullLabel); g.invokeVirtual(type, getMethod("String toString()")); g.goTo(afterToString); g.mark(nullLabel); g.pop(); g.push(("null")); g.mark(afterToString); } g.invokeVirtual(getType(StringBuilder.class), getMethod("StringBuilder append(String)")); g.pop(); } if (!end.isEmpty()) { g.dup(); g.push(end); g.invokeVirtual(getType(StringBuilder.class), getMethod("StringBuilder append(String)")); g.pop(); } g.invokeVirtual(getType(StringBuilder.class), getMethod("String toString()")); return type(ctx); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ExpressionToString that = (ExpressionToString) o; if (arguments != null ? !arguments.equals(that.arguments) : that.arguments != null) return false; return true; } @Override public int hashCode() { return (arguments != null ? arguments.hashCode() : 0); } }