/* * Copyright 2014 the original author or authors. * * 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 com.koloboke.jpsg.collect; import com.koloboke.jpsg.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.lang.String.format; import static com.koloboke.jpsg.collect.Permission.REMOVE; public abstract class MethodGenerator { public static String primitiveHash(PrimitiveType type, String value) { switch (type) { case BYTE: case SHORT: case CHAR: case INT: case FLOAT: return value; case LONG: case DOUBLE: return format("((int) (%s ^ (%s >>> 32)))", value, value); default: throw new IllegalStateException(); } } public static String wrap(MethodContext cxt, Option opt, String v) { if (cxt.internalVersion()) { // don't wrap, if internal version return v; } if (opt == PrimitiveType.FLOAT) v = "Float.intBitsToFloat(" + v + ")"; if (opt == PrimitiveType.DOUBLE) v = "Double.longBitsToDouble(" + v + ")"; return v; } public static String unwrap(MethodContext cxt, Option opt, String v) { if (cxt.internalVersion()) return v; if (opt == PrimitiveType.FLOAT) v = "Float.floatToIntBits(" + v + ")"; if (opt == PrimitiveType.DOUBLE) v = "Double.doubleToLongBits(" + v + ")"; return v; } private static final SimpleOption AS = new SimpleOption("as"); public static String defaultMethodName(MethodContext cxt, Method method) { String name = method.getClass().getSimpleName(); name = name.substring(0, 1).toLowerCase() + name.substring(1); if (cxt.isPrimitiveValue()) { Option suffix = cxt.getOption("suffix"); if (AS.equals(suffix)) { name += "As" + ((PrimitiveType) cxt.mapValueOption()).title; } } return name; } // Useful utils protected static int countOccurrences(String s, String sub) { Pattern p = Pattern.compile(Pattern.quote(sub)); Matcher m = p.matcher(s); int count = 0; while (m.find()) { count += 1; } return count; } protected static String replaceAll(String s, String sub, String repl) { return Pattern.compile(Pattern.quote(sub)).matcher(s).replaceAll(repl); } protected static String replaceFirst(String s, String sub, String repl) { return Pattern.compile(Pattern.quote(sub)).matcher(s).replaceFirst(repl); } protected final List<String> lines = new ArrayList<String>(); protected String indent = ""; public final MethodGenerator lines(String... lines) { for (String line : lines) { this.lines.add(indent + line); } return this; } protected final EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class); protected MethodContext cxt; public final String generate(MethodContext cxt, String indent, Method method) { this.cxt = cxt; method.init(this, this.cxt); this.indent = indent; generateLines(method); if (cxt.immutable() && !permissions.isEmpty() || cxt.updatable() && permissions.contains(REMOVE)) return this.indent + "throw new java.lang.UnsupportedOperationException();\n"; if (!this.indent.equals(indent)) throw new IllegalStateException( "Indent of start and end of the generated method doesn't match"); String body = ""; for (String line : lines) { body += line + "\n"; } return body; } protected abstract void generateLines(Method method); protected String wrapKey(String key) { return wrap(cxt, cxt.keyOption(), key); } protected String unwrapKey(String key) { return unwrap(cxt, cxt.keyOption(), key); } protected String wrapValue(String value) { return wrap(cxt, cxt.mapValueOption(), value); } protected String unwrapValue(String value) { return unwrap(cxt, cxt.mapValueOption(), value); } public final MethodGenerator indent() { indent += " "; return this; } public final MethodGenerator unIndent() { indent = indent.substring(4); return this; } public final MethodGenerator block() { String lastLine = lines.get(lines.size() - 1); lines.set(lines.size() - 1, lastLine + " {"); indent(); return this; } public final MethodGenerator ifBlock(String condition) { return lines("if (" + condition + ")").block(); } public final MethodGenerator elseBlock() { return unIndent().lines("} else").block(); } public final MethodGenerator elseIf(String condition) { return unIndent().lines("} else if (" + condition + ")").block(); } public final MethodGenerator blockEnd() { return unIndent().lines("}"); } public void ret(String ret) { lines("return " + ret + ";"); } public final void ret(boolean ret) { ret(ret + ""); } public final void concurrentMod() { lines("throw new java.util.ConcurrentModificationException();"); } public final void illegalState() { lines("throw new java.lang.IllegalStateException();"); } public final void unsupportedOperation(String message) { assert message != null && !message.isEmpty(); lines("throw new java.lang.UnsupportedOperationException(\"" + message + "\");"); } public final void requireNonNull(String obj) { lines("if (" + obj + " == null)"); lines(" throw new java.lang.NullPointerException();"); } public final void incrementModCount() { if (cxt.concurrentModificationChecked()) lines("incrementModCount();"); } protected int countUsages(int fromLine, String s) { int usages = 0; for (int i = fromLine; i < lines.size(); i++) { usages += countOccurrences(lines.get(i), s); } return usages; } protected void replaceAll(int fromLine, String placeholder, String replacement) { for (int i = fromLine; i < lines.size(); i++) { lines.set(i, replaceAll(lines.get(i), placeholder, replacement)); } } protected void replaceFirstDifferent(int bodyStart, String placeholder, String firstReplacement, String restReplacement) { boolean replacedFirst = false; for (int i = bodyStart; i < lines.size(); i++) { String line = lines.get(i); if (!replacedFirst) { String newLine = replaceFirst(line, placeholder, firstReplacement); if (!line.equals(newLine)) { replacedFirst = true; line = newLine; } } lines.set(i, replaceAll(line, placeholder, restReplacement)); } } }