/*
* Copyright 2014 Google Inc.
*
* 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.template.soy.data.ordainers;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.ImmutableSet;
import com.google.template.soy.data.SanitizedContent;
import com.google.template.soy.data.SanitizedContent.ContentKind;
import com.google.template.soy.data.UnsafeSanitizedContentOrdainer;
import java.util.regex.Pattern;
/**
* Creation utilities for SanitizedContent objects for JS identifiers.
*
* <p>This is based on http://mathiasbynens.be/notes/javascript-identifiers
*
*/
public final class JsIdentifierOrdainer {
/**
* Valid patterns for JS identifiers.
*
* <p>This does not accept zero-width characters.
*/
private static final Pattern VALID_JS_IDENTIFIER_PATTERN =
Pattern.compile(
"^[$_\\p{IsLetter}][$_\\p{IsLetter}\\p{IsDigit}]*$", Pattern.UNICODE_CHARACTER_CLASS);
/**
* Invalid JS identifiers.
*
* <p>Reserved words and other identifiers that are almost never a good idea to use.
*
* <p>TODO(Tony Payne): See if there is a canonical list somewhere.
*/
private static final ImmutableSet<String> INVALID_JS_IDENTIFIERS =
ImmutableSet.of(
/* reserved words */
"break",
"case",
"catch",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"finally",
"for",
"function",
"if",
"in",
"instanceof",
"new",
"return",
"switch",
"this",
"throw",
"try",
"typeof",
"var",
"void",
"while",
"with",
/* future reserved words */
"class",
"const",
"enum",
"export",
"extends",
"import",
"super",
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"yield",
/* literals */
"null",
"true",
"false",
"NaN",
"Infinity",
"undefined",
/* other words to avoid */
"eval",
"arguments",
"int",
"byte",
"char",
"goto",
"long",
"final",
"float",
"short",
"double",
"native",
"throws",
"boolean",
"abstract",
"volatile",
"transient",
"synchronized",
"prototype",
"__proto__");
/** No constructor. */
private JsIdentifierOrdainer() {}
/**
* Validates that {@code identifier} matches a safe pattern for JS identifiers and ordains the
* value as JS.
*
* <p>TODO: this appears to be redundant with some code in JsSrcUtils.
*/
public static SanitizedContent jsIdentifier(String identifier) {
checkArgument(
VALID_JS_IDENTIFIER_PATTERN.matcher(identifier).matches(),
"JS identifier '%s' should match the pattern '%s'",
identifier,
VALID_JS_IDENTIFIER_PATTERN.pattern());
checkArgument(
!INVALID_JS_IDENTIFIERS.contains(identifier),
"JS identifier '%s' should not be a reserved word or match a literal",
identifier);
return UnsafeSanitizedContentOrdainer.ordainAsSafe(identifier, ContentKind.JS);
}
}