/* * 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 com.koloboke.jpsg.collect.algo.hash.*; import com.koloboke.jpsg.collect.bulk.*; import com.koloboke.jpsg.collect.iter.IterMethod; import com.koloboke.jpsg.collect.mapqu.*; import java.io.PrintWriter; import java.io.StringWriter; import java.util.*; import static java.lang.String.format; public final class MethodGeneratingProcessor extends TemplateProcessor { private static final int PRIORITY = DEFAULT_PRIORITY - 100; private static final String METHOD_BODY_PREFIX = "(?<indentSpaces>[^\\S\\n]*+)/[\\*/]\\s*template"; private static final CheckingPattern METHOD_BODY_P = CheckingPattern.compile(METHOD_BODY_PREFIX, METHOD_BODY_PREFIX + "\\s+(?<methodName>\\S+)" + format("\\s*(with%s)?", Generator.Companion.getDIMENSIONS()) + "\\s*[\\*/]/" + "([^\\S\\n]*$|[^/]*?/[\\*/]\\s*endtemplate\\s*[\\*/]/)"); private static final Map<String, Class<? extends Method>> METHODS = new HashMap<String, Class<? extends Method>>() { { // bulk put(ForEachWhile.class); put(ReversePutAllTo.class); put(ReverseAddAllTo.class); put(ReverseRemoveAllFrom.class); put(AllContainingIn.class); put(RemoveIf.class); put(ReplaceAll.class); put(ForEach.class); put(ToArray.class); put(ToTypedArray.class); put(ToPrimitiveArray.class); put(RemoveAll.class); put(RetainAll.class); put(SetHashCode.class); put(ListHashCode.class); put(ToString.class); // hash-only put(Rehash.class); put(Index.class); put(Insert.class); put(ValueIndex.class); put(LHashRemoveAt.class); put(LHashCloseDelayedRemoved.class); // map query/update put(ContainsEntry.class); put(ContainsKey.class); put(Get.class); put(GetOrDefault.class); put(Remove.class); put(JustRemove.class); put(RemoveEntry.class); put(Put.class); put(PutIfAbsent.class); put(JustPut.class); put(AddValue.class); put(AddValueWithInitial.class); put(Compute.class); put(ComputeIfAbsent.class); put(ComputeIfPresent.class); put(Merge.class); put(Replace.class); put(ReplaceEntry.class); put(Add.class); } private void put(Class<? extends Method> c) { put(c.getSimpleName().toLowerCase(), c); } }; private static final Map<String, Map<Class, Class<? extends MethodGenerator>>> GENERATORS_BY_ALGO_FAMILY = new HashMap<String, Map<Class, Class<? extends MethodGenerator>>>() {{ put( "hash", new HashMap<Class, Class<? extends MethodGenerator>>() { { put(HashBulkMethodGenerator.class); put(HashMapQueryUpdateMethodGenerator.class); put(LHashRemoveAt.MethodGenerator.class); put(LHashCloseDelayedRemoved.MethodGenerator.class); } private void put(Class<? extends MethodGenerator> c) { Class key = MethodGenerator.class.equals(c.getSuperclass()) ? c : c.getSuperclass(); put(key, c); } } ); }}; private static String generate(String methodName, Context cxt, String indent) { Method method; MethodGenerator generator; if (methodName.toLowerCase().startsWith("iterator.")) { method = IterMethod.forName(methodName.split("\\.")[1]); generator = new HashIteratorMethodGenerator(); } else if (methodName.toLowerCase().startsWith("cursor.")) { method = IterMethod.forName(methodName.split("\\.")[1]); generator = new HashCursorMethodGenerator(); } else { Class<? extends Method> methodClass = METHODS.get(methodName.toLowerCase()); if (methodClass == null) { throw new RuntimeException("Unknown method: " + methodName); } try { method = methodClass.newInstance(); Class<? extends MethodGenerator> generatorClass = GENERATORS_BY_ALGO_FAMILY.get("hash").get(method.generatorBase()); generator = generatorClass.newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } return generator.generate(new MethodContext(cxt), indent, method); } @Override public int priority() { return PRIORITY; } @Override protected void process(StringBuilder builder, Context source, Context target, String template) { CheckingMatcher matcher = METHOD_BODY_P.matcher(template); StringBuilder sb = new StringBuilder(); while (matcher.find()) { String methodName = matcher.group("methodName"); String indentSpaces = matcher.group("indentSpaces"); String additionalContextDims = matcher.group("dimensions"); if (additionalContextDims != null) { List<Context> addContexts = getDimensionsParser().parseForContext(additionalContextDims) .generateContexts(); if (addContexts.size() != 1) { throw new IllegalStateException(); } target = target.join(addContexts.get(0)); } try { String generated = generate(methodName, target, indentSpaces); matcher.appendSimpleReplacement(sb, generated.replaceFirst("\\s+$", "")); } catch (RuntimeException e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); throw MalformedTemplateException.near(template, matcher.start(), "Source file: " + Generator.currentSourceFile() + "\n" + "Runtime exception while generating " + methodName + " template:\n" + sw.toString()); } } matcher.appendTail(sb); postProcess(builder, source, target, sb.toString()); } }