/**
*
* Copyright (c) 2006-2017, Speedment, Inc. All Rights Reserved.
*
* 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.speedment.common.codegen.util;
import static com.speedment.common.codegen.internal.util.NullUtil.requireNonNullElements;
import static com.speedment.common.codegen.internal.util.StaticClassUtil.instanceNotAllowed;
import java.util.Arrays;
import java.util.List;
import static java.util.Objects.requireNonNull;
import java.util.Optional;
import java.util.function.Function;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import java.util.stream.Stream;
/**
* Common formatting methods used when generating code.
*
* @author Emil Forslund
*/
public final class Formatting {
/**
* Returns a string consisting of the specified blocks concatenated
* and separated by the specified separator.
*
* @param separator The separator.
* @param blocks All the blocks.
* @return The concatenated string.
*/
public static String separate(String separator, String... blocks) {
requireNonNullElements(blocks);
return Stream.of(blocks)
.collect(joining(separator));
}
/**
* Returns the specified text but with the first character lowercase.
*
* @param input The text.
* @return The resulting text.
*/
public static String lcfirst(String input) {
return withFirst(input, (first) -> String.valueOf(Character.toLowerCase(first)));
}
/**
* Returns the specified text but with the first character uppercase.
*
* @param input The text.
* @return The resulting text.
*/
public static String ucfirst(String input) {
return withFirst(input, (first) -> String.valueOf(Character.toUpperCase(first)));
}
/**
* Does something with the first character in the specified String.
*
* @param input The String.
* @param callback The something.
* @return The new String.
*/
public static String withFirst(String input, Function<Character, String> callback) {
if (input == null) {
return null;
} else if (input.length() == 0) {
return "";
} else {
return String.join("",
callback.apply(input.charAt(0)),
input.subSequence(1, input.length())
);
}
}
/**
* Repeats the specified substring count times.
* @param str The string to repeat.
* @param count The number of times to repeat it.
* @return The new String.
*/
public static String repeat(String str, int count) {
final StringBuilder result = new StringBuilder();
for (int i = 0; i < count; i++) {
result.append(str);
}
return result.toString();
}
/**
* Indents the specified text, surrounds it with brackets and put the
* content on a separate line.
*
* @param text The text to surround.
* @return The text with a '{\n' before and a '\n}' afterwards.
*/
public static String block(String text) {
return "{" + nl + indent(text) + nl + "}";
}
/**
* Indents the specified text, surrounds it with brackets and put the
* content on a separate line.
*
* @param row the first row of the text.
* @param rows the rest of the rows.
* @return the text with a '{\n' before and a '\n}' afterwards.
*/
public static String block(String row, String... rows) {
requireNonNull(row);
requireNonNullElements(rows);
return block(
Arrays.stream(rows).collect(joining(
nl(), row + nl(), "")
)
);
}
/**
* Indents the specified text, surrounds it with brackets and put the
* content on a separate line.
*
* @param rows the rest of the rows.
* @return the text with a '{\n' before and a '\n}' afterwards.
*/
public static String block(Stream<String> rows) {
return block(rows.collect(joining(nl())));
}
/**
* Indents one level after each new-line-character as defined by
* <code>nl()</code>.
*
* @param text the text to indent.
* @return the indented text.
*/
public static String indent(String text) {
return indent + text.replaceAll("\\r?\\n", nl + indent);
}
/**
* Indents one level after each new-line-character as defined by
* <code>nl()</code>. If multiple strings are specified, new-line characters
* will be added between them.
*
* @param text the text to indent.
* @return the indented text.
*/
public static String indent(String... text) {
return indent + String.join(nl(), text).replaceAll("\\r?\\n", nl + indent);
}
/**
* Indents one level after each new-line-character as defined by
* <code>nl()</code>. f multiple strings are specified, new-line characters
* will be added between them.
*
* @param text the text to indent.
* @return the indented text.
*/
public static String indent(Stream<String> text) {
return indent(text.toArray(String[]::new));
}
/**
* Indents a variable number of levels level after each new-line-character
* as defined by <code>nl()</code>.
*
* @param text the text to indent
* @param steps the number of steps to indent
* @return the indented text
*/
public static String indent(String text, int steps) {
switch (steps) {
case 0 : return text;
case 1 : return indent(text);
default : return indent(indent(text, steps - 1));
}
}
/**
* If the condition is present, the true-function will be called
* with its value and the result will be returned. Else the
* false-value will be returned.
* @param <E> The inner type of the condition.
* @param <R> The result type.
* @param condition The condition to be evaluated for presence.
* @param trueMap The function that will be called if present.
* @param falseValue The value returned if the condition is not present.
* @return The value.
*/
public static <E, R> R ifelse(Optional<E> condition, Function<E, R> trueMap, R falseValue) {
if (condition.isPresent()) {
return trueMap.apply(condition.get());
} else {
return falseValue;
}
}
/**
* Returns the new-line-character as defined in <code>nl(String)</code>.
*
* @return The new-line character.
*/
public static String nl() {
return nl;
}
/**
* Sets which new-line-character to use. Should be set before any code
* generation happens for indentation to work as expected.
*
* @param nl The new character to use.
*/
public static void nl(String nl) {
Formatting.nl = nl;
Formatting.dnl = nl + nl;
}
/**
* Returns two new-line-characters as defined in <code>nl(String)</code>.
*
* @return The new-line character.
*/
public static String dnl() {
return dnl;
}
/**
* Returns the current tab character. This can be defined by using
* <code>tab(String)</code>.
*
* @return The current tab character.
*/
public static String tab() {
return indent;
}
/**
* Sets the tab character to use when generating code. This should only be
* called before any indentation occurs to prevent errors.
*
* @param tab The new tab character.
*/
public static void tab(String tab) {
Formatting.indent = tab;
}
/**
* Returns the 'name' part of a long name. This is everything after the last
* dot.
*
* @param longName The long name.
* @return The name part.
*/
public static String shortName(String longName) {
final String temp = longName.replace('$', '.');
if (temp.contains(".")) {
return temp.substring(temp.lastIndexOf(".") + 1);
} else {
return temp;
}
}
/**
* Returns the 'package' part of a long name. This is everything before the
* last dot.
*
* @param longName The long name.
* @return The package part.
*/
public static Optional<String> packageName(String longName) {
if (longName.contains(".")) {
return Optional.of(longName.substring(0,
longName.lastIndexOf(".")
));
} else {
return Optional.empty();
}
}
public static Optional<String> fileToClassName(String fileName) {
Optional<String> result = Optional.empty();
if (fileName.endsWith(".java")) {
String className = fileName;
className = className.replace('/', '.');
className = className.replace('\\', '.');
className = className.substring(0, className.length() - 5);
result = Optional.of(className);
}
return result;
}
public static String classToJavaFileName(String longName) {
return longName.replace('.', '/') + ".java";
}
/**
* Removes the generics and array parts of a class name.
*
* @param className the full class name
* @return class name with decorations stripped
*/
public static String stripGenerics(String className) {
String name = className;
if (name.contains("<")) {
name = name.substring(0, name.indexOf("<"));
}
if (name.contains("[")) {
name = name.substring(0, name.indexOf("["));
}
return name;
}
/**
* Replaces every tab character ({@code \t}) in the specified rows with a
* number spaces so that the character indexes align. This is called
* recursively until all tab characters have been replaced. If there are no
* tabs in the text, the method has no effect.
* <p>
* This implementation will begin by splitting the specified string into
* rows, then apply the alignment. Otherwise it is the same as
* {@link #alignTabs(List)}.
*
* @param rows a single string with all the rows to align
* @return the same string but with tabs replaced with aligning spaces
*/
public static String alignTabs(String rows) {
final List<String> list = Stream.of(rows.split(nl())).collect(toList());
alignTabs(list);
return list.stream().collect(joining(nl()));
}
/**
* Replaces every tab character ({@code \t}) in the specified rows with a
* number spaces so that the character indexes align. This is called
* recursively until all tab characters have been replaced. If there are no
* tabs in the text, the method has no effect.
* <p>
* <em>Example:</em>
* If you call the method on the following code:
* {@code
* this.firstname\t = firstname;\t // Nullable
* this.lastname\t = lastname;\t // Nullable
* this.email\t = email;\t // Nullable
* this.age\t = age;
* }
*
* You will get the following result:
* {@code
* this.firstname = firstname; // Nullable
* this.lastname = lastname; // Nullable
* this.email = email; // Nullable
* this.age = age;
* }
*
* @param rows list of rows to operate on
*/
public static void alignTabs(List<String> rows) {
while (true) {
int maxIndex = -1;
for (final String row : rows) {
final int index = row.indexOf('\t');
if (index > maxIndex) {
maxIndex = index;
}
}
if (maxIndex > -1) {
for (int i = 0; i < rows.size(); i++) {
final String row = rows.get(i);
final int index = row.indexOf('\t');
if (index > -1) {
rows.set(i, row.replaceFirst("\t", Formatting.repeat(" ", maxIndex - index)));
}
}
} else {
break;
}
}
}
private static String
nl = "\n",
dnl = "\n\n",
indent = " ";
/**
* Utility classes should not be instantiated.
*/
private Formatting() { instanceNotAllowed(getClass()); }
}