/* * Copyright 2012-2015 Sergey Ignatov * * 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 org.intellij.erlang.build; import com.intellij.openapi.util.Pair; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import java.util.List; class ErlangModuleTextGenerator { private ErlangModuleTextGenerator() { } @NotNull public static ErlangModuleTextBuilder module(@NotNull String moduleName) { return new ErlangModuleTextBuilder(moduleName); } @NotNull public static ParseTransformBuilder pt(@NotNull String moduleName) { return new ParseTransformBuilder(moduleName); } @NotNull public static BehaviourBuilder behaviour(@NotNull String moduleName) { return new BehaviourBuilder(moduleName); } static class ErlangModuleTextBuilder { private final String myModuleName; private final List<Pair<String, Integer>> myExports = ContainerUtil.newArrayList(); private final List<BehaviourBuilder> myBehaviours = ContainerUtil.newArrayList(); private final List<String> myParseTransforms = ContainerUtil.newArrayList(); private final List<String> myIncludes = ContainerUtil.newArrayList(); private final List<String> myIncludeLibs = ContainerUtil.newArrayList(); public ErlangModuleTextBuilder(@NotNull String moduleName) { myModuleName = moduleName; } public String getModuleName() { return myModuleName; } public ErlangModuleTextBuilder pt(@NotNull String moduleName) { myParseTransforms.add(moduleName); return this; } public ErlangModuleTextBuilder behaviour(@NotNull BehaviourBuilder behaviour) { myBehaviours.add(behaviour); myExports.addAll(behaviour.myCallbacks); return this; } public ErlangModuleTextBuilder include(@NotNull String modulePath) { myIncludes.add(modulePath); return this; } public ErlangModuleTextBuilder includeLib(@NotNull String modulePath) { myIncludeLibs.add(modulePath); return this; } @NotNull public String build() { StringBuilder builder = new StringBuilder(); appendModule(builder); appendIncludes(builder); appendIncludeLibs(builder); appendBehaviour(builder); appendParseTransforms(builder); appendExports(builder); build(builder); return builder.toString(); } private void appendIncludeLibs(StringBuilder builder) { for (String includeLib : myIncludeLibs) { builder.append("-includeLib(\"").append(includeLib).append("\").\n"); } } private void appendIncludes(StringBuilder builder) { for (String include : myIncludes) { builder.append("-include(\"").append(include).append("\").\n"); } } protected void build(@NotNull StringBuilder builder) { appendFunctions(builder); } @NotNull protected static <T> StringBuilder commaSeparated(@NotNull StringBuilder sb, @NotNull List<T> items, @NotNull ItemFormatter<T> formatter) { String separator = ", "; for (T item : items) { formatter.format(sb, item).append(separator); } sb.setLength(items.isEmpty() ? sb.length() : sb.length() - separator.length()); return sb; } private void appendFunctions(@NotNull StringBuilder builder) { for (Pair<String, Integer> functionEntry : myExports) { appendFunction(builder, functionEntry.first, functionEntry.second); } } private void appendExports(@NotNull StringBuilder builder) { if (myExports.isEmpty()) return; builder.append("-export(["); commaSeparated(builder, myExports, (sb, export) -> sb.append(export.first).append("/").append(export.second)); builder.append("]).\n"); } private void appendModule(@NotNull StringBuilder builder) { builder.append("-module(").append(myModuleName).append(").\n"); } private void appendBehaviour(@NotNull StringBuilder builder) { for (BehaviourBuilder behaviour : myBehaviours) { builder.append("-behaviour(").append(behaviour.getModuleName()).append(").\n"); } } private static void appendFunction(@NotNull StringBuilder builder, @NotNull String functionName, int arity) { List<Integer> argumentIndices = ContainerUtil.newArrayListWithCapacity(arity); for (int i = 0; i < arity; i++) { argumentIndices.add(i); } builder.append(functionName).append("("); commaSeparated(builder, argumentIndices, (sb, argumentIdx) -> sb.append("_Arg").append(argumentIdx)); builder.append(") -> ok.\n"); } private void appendParseTransforms(@NotNull StringBuilder builder) { if (myParseTransforms.isEmpty()) return; builder.append("-compile(["); commaSeparated(builder, myParseTransforms, (sb, pt) -> sb.append("{parse_transform, ").append(pt).append("}")); builder.append("]).\n"); } protected interface ItemFormatter<T> { @NotNull StringBuilder format(@NotNull StringBuilder sb, @NotNull T item); } } static class ParseTransformBuilder extends ErlangModuleTextBuilder { public ParseTransformBuilder(@NotNull String moduleName) { super(moduleName); } @Override protected void build(@NotNull StringBuilder builder) { createTransform(builder); super.build(builder); } private static void createTransform(@NotNull StringBuilder builder) { builder.append("-export([parse_transform/2]).\n"); builder.append("parse_transform(Forms, _Options) -> Forms.\n"); } } static class BehaviourBuilder extends ErlangModuleTextBuilder { private final List<Pair<String, Integer>> myCallbacks = ContainerUtil.newArrayList(); public BehaviourBuilder(@NotNull String moduleName) { super(moduleName); } public BehaviourBuilder callback(@NotNull String name, int arity) { myCallbacks.add(Pair.createNonNull(name, arity)); return this; } @Override protected void build(@NotNull StringBuilder builder) { builder.append("-export([behaviour_info/1]).\n"); builder.append("behaviour_info(callbacks) ->["); commaSeparated(builder, myCallbacks, (sb, callback) -> sb.append("{").append(callback.first).append(", ").append(callback.second).append("}")); builder.append("].\n"); super.build(builder); } } }