/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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.asakusafw.operator.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
/**
* Represents a simple name in Java.
*/
public class JavaName {
private static final Set<String> RESERVED;
static {
// see http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.9
Set<String> set = new HashSet<>();
set.add("abstract"); //$NON-NLS-1$
set.add("continue"); //$NON-NLS-1$
set.add("for"); //$NON-NLS-1$
set.add("new"); //$NON-NLS-1$
set.add("switch"); //$NON-NLS-1$
set.add("assert"); //$NON-NLS-1$
set.add("default"); //$NON-NLS-1$
set.add("if"); //$NON-NLS-1$
set.add("package"); //$NON-NLS-1$
set.add("synchronized"); //$NON-NLS-1$
set.add("boolean"); //$NON-NLS-1$
set.add("do"); //$NON-NLS-1$
set.add("goto"); //$NON-NLS-1$
set.add("private"); //$NON-NLS-1$
set.add("this"); //$NON-NLS-1$
set.add("break"); //$NON-NLS-1$
set.add("double"); //$NON-NLS-1$
set.add("implements"); //$NON-NLS-1$
set.add("protected"); //$NON-NLS-1$
set.add("throw"); //$NON-NLS-1$
set.add("byte"); //$NON-NLS-1$
set.add("else"); //$NON-NLS-1$
set.add("import"); //$NON-NLS-1$
set.add("public"); //$NON-NLS-1$
set.add("throws"); //$NON-NLS-1$
set.add("case"); //$NON-NLS-1$
set.add("enum"); //$NON-NLS-1$
set.add("instanceof"); //$NON-NLS-1$
set.add("return"); //$NON-NLS-1$
set.add("transient"); //$NON-NLS-1$
set.add("catch"); //$NON-NLS-1$
set.add("extends"); //$NON-NLS-1$
set.add("int"); //$NON-NLS-1$
set.add("short"); //$NON-NLS-1$
set.add("try"); //$NON-NLS-1$
set.add("char"); //$NON-NLS-1$
set.add("final"); //$NON-NLS-1$
set.add("interface"); //$NON-NLS-1$
set.add("static"); //$NON-NLS-1$
set.add("void"); //$NON-NLS-1$
set.add("class"); //$NON-NLS-1$
set.add("finally"); //$NON-NLS-1$
set.add("long"); //$NON-NLS-1$
set.add("strictfp"); //$NON-NLS-1$
set.add("volatile"); //$NON-NLS-1$
set.add("const"); //$NON-NLS-1$
set.add("float"); //$NON-NLS-1$
set.add("native"); //$NON-NLS-1$
set.add("super"); //$NON-NLS-1$
set.add("while"); //$NON-NLS-1$
RESERVED = Collections.unmodifiableSet(set);
}
private static final String EMPTY_NAME = "_"; //$NON-NLS-1$
private final List<String> words;
JavaName(List<String> words) {
this.words = normalize(Objects.requireNonNull(words, "words must not be null")); //$NON-NLS-1$
}
/**
* Returns a Java name represented in the specified string.
* @param nameString a string which represents a Java name
* @return related object
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public static JavaName of(String nameString) {
Objects.requireNonNull(nameString, "nameString must not be null"); //$NON-NLS-1$
if (nameString.isEmpty()) {
throw new IllegalArgumentException("nameString must not be empty"); //$NON-NLS-1$
} else if (nameString.indexOf('_') >= 0 || nameString.toUpperCase(Locale.ENGLISH).equals(nameString)) {
String[] segments = nameString.split(EMPTY_NAME);
return new JavaName(Arrays.asList(segments));
} else {
List<String> segments = new ArrayList<>();
int start = 0;
for (int i = 1, n = nameString.length(); i < n; i++) {
if (Character.isUpperCase(nameString.charAt(i))) {
segments.add(nameString.substring(start, i));
start = i;
}
}
segments.add(nameString.substring(start));
return new JavaName(segments);
}
}
/**
* Returns name segments as lower case characters.
* @return name segments
*/
public List<String> getSegments() {
return words;
}
/**
* Returns this name in form of type names ({@code CamelCase}).
* @return CamelCase name
*/
public String toTypeName() {
if (words.isEmpty()) {
return EMPTY_NAME;
}
StringBuilder buf = new StringBuilder();
for (int i = 0, n = words.size(); i < n; i++) {
buf.append(capitalize(words.get(i)));
}
return buf.toString();
}
/**
* Returns this name in form of member names ({@code camelCase}).
* @return camelCase name
*/
public String toMemberName() {
if (words.isEmpty()) {
return EMPTY_NAME;
}
StringBuilder buf = new StringBuilder();
buf.append(words.get(0));
for (int i = 1, n = words.size(); i < n; i++) {
buf.append(capitalize(words.get(i)));
}
String result = buf.toString();
if (RESERVED.contains(result) || Character.isJavaIdentifierStart(result.charAt(0)) == false) {
return escape(result);
}
return result;
}
private String escape(String result) {
assert result != null;
return result + '_';
}
/**
* Returns this name in form of constant names ({@code UPPER_CASE}).
* @return UPPER_CASE name
*/
public String toConstantName() {
if (words.isEmpty()) {
return EMPTY_NAME;
}
return toSnakeName().toUpperCase(Locale.ENGLISH);
}
/**
* Returns this name in form of constant names ({@code snake_case}).
* @return snake_case name
*/
public String toSnakeName() {
if (words.isEmpty()) {
return EMPTY_NAME;
}
StringBuilder buf = new StringBuilder();
buf.append(words.get(0));
for (int i = 1, n = words.size(); i < n; i++) {
buf.append('_');
buf.append(words.get(i));
}
return buf.toString();
}
/**
* Inserts a name segment into beginning of this name.
* @param segment the name segment
* @throws IllegalArgumentException if the name segment is not valid
*/
public void addFirst(String segment) {
words.add(0, normalize(segment));
}
/**
* Removes the first name segment in this name.
* @throws IllegalStateException if this name is empty
*/
public void removeFirst() {
if (words.isEmpty()) {
throw new IllegalStateException();
}
words.remove(0);
}
/**
* Inserts a name segment into ending of this name.
* @param segment the name segment
* @throws IllegalArgumentException if the name segment is not valid
*/
public void addLast(String segment) {
words.add(normalize(segment));
}
/**
* Removes the last name segment in this name.
* @throws IllegalStateException if this name is empty
*/
public void removeLast() {
if (words.isEmpty()) {
throw new IllegalStateException();
}
words.remove(words.size() - 1);
}
private String capitalize(String segment) {
assert segment != null;
StringBuilder buf = new StringBuilder(segment);
buf.setCharAt(0, Character.toUpperCase(buf.charAt(0)));
return buf.toString();
}
private static String normalize(String segment) {
Objects.requireNonNull(segment, "segment must not be null"); //$NON-NLS-1$
if (segment.isEmpty()) {
throw new IllegalArgumentException();
}
return segment.toLowerCase(Locale.ENGLISH);
}
private static List<String> normalize(List<String> segments) {
assert segments != null;
List<String> results = new ArrayList<>();
for (String segment : segments) {
if (segment.isEmpty() == false) {
results.add(normalize(segment));
}
}
return results;
}
}