com.google.javascript.jscomp.GoogleCodingConvention Maven / Gradle / Ivy
/*
* Copyright 2007 The Closure Compiler 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.google.javascript.jscomp;
import com.google.common.annotations.GwtIncompatible;
import com.google.errorprone.annotations.Immutable;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.StaticSourceFile;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This describes the Google-specific JavaScript coding conventions. Within Google, variable names
* are semantically significant.
*/
@Immutable
public class GoogleCodingConvention extends CodingConventions.Proxy {
private static final long serialVersionUID = 1L;
private static final String OPTIONAL_ARG_PREFIX = "opt_";
private static final String VAR_ARGS_NAME = "var_args";
private static final Pattern ENUM_KEY_PATTERN =
Pattern.compile("[A-Z0-9][A-Z0-9_]*");
private static final Pattern PACKAGE_WITH_TEST_DIR =
Pattern.compile("^(.*)/(?:test|tests|testing)/(?:[^/]+)$");
private static final Pattern GENFILES_DIR = Pattern.compile("-out/.*/(bin|genfiles)/(.*)$");
/** By default, decorate the ClosureCodingConvention. */
public GoogleCodingConvention() {
this(new ClosureCodingConvention());
}
/** Decorates a wrapped CodingConvention. */
public GoogleCodingConvention(CodingConvention convention) {
super(convention);
}
/**
* {@inheritDoc}
*
* This enforces the Google const name convention, that the first character
* after the last $ must be an upper-case letter and all subsequent letters
* must be upper case. The name must be at least 2 characters long.
*
*
Examples:
*
* aaa Not constant - lower-case letters in the name
* A Not constant - too short
* goog$A Constant - letters after the $ are upper-case.
* AA17 Constant - digits can appear after the first letter
* goog$7A Not constant - first character after the $ must be
* upper case.
* $A Constant - doesn't have to be anything in front of the $
*
*/
@Override
public boolean isConstant(String name) {
if (name.length() <= 1) {
return false;
}
// In compiled code, '$' is often a namespace delimiter. To allow inlining
// of namespaced constants, we strip off any namespaces here.
int pos = name.lastIndexOf('$');
if (pos >= 0) {
name = name.substring(pos + 1);
if (name.isEmpty()) {
return false;
}
}
return isConstantKey(name);
}
@Override
public boolean isConstantKey(String name) {
if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) {
return false;
}
// hack way of checking that there aren't any lower-case letters
return name.toUpperCase(Locale.ROOT).equals(name);
}
/**
* {@inheritDoc}
*
* This enforces Google's convention about enum key names. They must match
* the regular expression {@code [A-Z0-9][A-Z0-9_]*}.
*
*
Examples:
*
* - A
* - 213
* - FOO_BAR
*
*/
@Override
public boolean isValidEnumKey(String key) {
return ENUM_KEY_PATTERN.matcher(key).matches();
}
/**
* {@inheritDoc}
*
* In Google code, parameter names beginning with {@code opt_} are
* treated as optional arguments.
*/
@Override
public boolean isOptionalParameter(Node parameter) {
return super.isOptionalParameter(parameter)
|| (parameter.isName() && parameter.getString().startsWith(OPTIONAL_ARG_PREFIX));
}
@Override
public boolean isVarArgsParameter(Node parameter) {
return super.isVarArgsParameter(parameter)
|| (parameter.isName() && VAR_ARGS_NAME.equals(parameter.getString()));
}
/**
* {@inheritDoc}
*
*
In Google code, any global name starting with an underscore is
* considered exported.
*/
@Override
public boolean isExported(String name, boolean local) {
return super.isExported(name, local) || (!local && name.startsWith("_"));
}
@Override
public boolean isClassFactoryCall(Node callNode) {
Node callTarget = callNode.getFirstChild();
return super.isClassFactoryCall(callNode)
|| (callTarget.isName() && callTarget.toString().equals("Polymer"));
}
/**
* {@inheritDoc}
*
*
In Google code, the package name of a source file is its file path.
* Exceptions: if a source file's parent directory is "test", "tests", or
* "testing", that directory is stripped from the package name.
* If a file is generated, strip the "genfiles" prefix to try
* to match the package of the generating file.
*/
@Override
@GwtIncompatible // TODO(tdeegan): Remove use of Matcher#group to make this fully GWT compatible.
public String getPackageName(StaticSourceFile source) {
String name = source.getName();
Matcher genfilesMatcher = GENFILES_DIR.matcher(name);
if (genfilesMatcher.find()) {
name = genfilesMatcher.group(2);
}
Matcher m = PACKAGE_WITH_TEST_DIR.matcher(name);
if (m.find()) {
return m.group(1);
} else {
int lastSlash = name.lastIndexOf('/');
return lastSlash == -1 ? "" : name.substring(0, lastSlash);
}
}
/**
* {@inheritDoc}
*
*
In Google code, private names end with an underscore, and exported
* names are never considered private (see {@link #isExported}).
*/
@Override
public boolean isPrivate(String name) {
return name.endsWith("_") && !name.endsWith("__") && !isExported(name);
}
@Override
public boolean hasPrivacyConvention() {
return true;
}
}