List of usage examples for com.google.common.base CaseFormat LOWER_CAMEL
CaseFormat LOWER_CAMEL
To view the source code for com.google.common.base CaseFormat LOWER_CAMEL.
Click Source Link
From source file:com.attribyte.relay.wp.WPSupplier.java
@Override public void init(final Properties props, final Optional<ByteString> savedState, final Logger logger) throws Exception { if (isInit.compareAndSet(false, true)) { this.logger = logger; logger.info("Initializing WP supplier..."); final long siteId = Long.parseLong(props.getProperty("siteId", "0")); if (siteId < 1L) { throw new Exception("A 'siteId' must be specified"); }//from w w w .jav a 2s. c o m final Set<String> cachedTaxonomies = ImmutableSet.of(TAG_TAXONOMY, CATEGORY_TAXONOMY); final Duration taxonomyCacheTimeout = Duration.ofMinutes(30); //TODO: Configure final Duration userCacheTimeout = Duration.ofMinutes(30); //TODO: Configure initPools(props, logger); this.db = new DB(defaultConnectionPool, siteId, cachedTaxonomies, taxonomyCacheTimeout, userCacheTimeout); Properties siteProps = new InitUtil("site.", props, false).getProperties(); Site overrideSite = new Site(siteProps); this.site = this.db.selectSite().overrideWith(overrideSite); logger.info( String.format("Initialized site %d - %s", this.site.id, Strings.nullToEmpty(this.site.title))); if (savedState.isPresent()) { startMeta = PostMeta.fromBytes(savedState.get()); } else { startMeta = PostMeta.ZERO; } this.selectSleepMillis = Integer.parseInt(props.getProperty("selectSleepMillis", "30000")); this.selectAll = props.getProperty("selectAll", "false").equalsIgnoreCase("true"); if (this.selectAll) { this.stopId = this.db.selectMaxPostId(); } else { this.stopId = 0L; } String supplyIdsFileStr = props.getProperty("supplyIdsFile", ""); if (!supplyIdsFileStr.isEmpty()) { this.selectAll = true; File supplyIdsFile = new File(supplyIdsFileStr); logger.info(String.format("Using 'supplyIdsFile', '%s'", supplyIdsFile.getAbsolutePath())); this.supplyIds = Lists.newArrayListWithExpectedSize(1024); List<String> lines = Files.readLines(supplyIdsFile, Charsets.UTF_8); for (String line : lines) { line = line.trim(); if (line.isEmpty() || line.startsWith("#")) { continue; } Long id = Longs.tryParse(line); if (id != null) { this.supplyIds.add(id); logger.info(String.format("Adding supplied id, '%d'", id)); } } } else { this.supplyIds = null; } this.maxSelected = Integer.parseInt(props.getProperty("maxSelected", "500")); String allowedStatusStr = props.getProperty("allowedStatus", "").trim(); if (!allowedStatusStr.isEmpty()) { if (allowedStatusStr.equalsIgnoreCase("all")) { this.allowedStatus = ALL_STATUS; } else { this.allowedStatus = ImmutableSet .copyOf(Splitter.on(',').omitEmptyStrings().splitToList(allowedStatusStr).stream() .map(Post.Status::fromString).collect(Collectors.toSet())); } } else { this.allowedStatus = DEFAULT_ALLOWED_STATUS; } String allowedTypesStr = props.getProperty("allowedTypes", "").trim(); if (!allowedStatusStr.isEmpty()) { this.allowedTypes = ImmutableSet .copyOf(Splitter.on(',').omitEmptyStrings().splitToList(allowedTypesStr)).stream() .map(Post.Type::fromString).collect(Collectors.toSet()); } else { this.allowedTypes = DEFAULT_ALLOWED_TYPES; } String allowedPostMetaStr = props.getProperty("allowedPostMeta", "").trim(); if (!allowedPostMetaStr.isEmpty()) { this.allowedPostMeta = ImmutableSet .copyOf(Splitter.on(',').omitEmptyStrings().splitToList(allowedPostMetaStr)); } else { this.allowedPostMeta = ImmutableSet.of(); } String allowedImageMetaStr = props.getProperty("allowedImageMeta", "").trim(); if (!allowedImageMetaStr.isEmpty()) { this.allowedImageMeta = ImmutableSet .copyOf(Splitter.on(',').omitEmptyStrings().splitToList(allowedImageMetaStr)); } else { this.allowedImageMeta = ImmutableSet.of(); } String allowedUserMetaStr = props.getProperty("allowedUserMeta", "").trim(); if (!allowedUserMetaStr.isEmpty()) { this.allowedUserMeta = ImmutableSet .copyOf(Splitter.on(',').omitEmptyStrings().splitToList(allowedUserMetaStr)); } else { this.allowedUserMeta = ImmutableSet.of(); } String metaNameCaseFormat = props.getProperty("metaNameCaseFormat", "").trim().toLowerCase(); switch (metaNameCaseFormat) { case "lower_camel": this.metaNameCaseFormat = CaseFormat.LOWER_CAMEL; break; case "lower_hyphen": this.metaNameCaseFormat = CaseFormat.LOWER_HYPHEN; break; case "upper_camel": this.metaNameCaseFormat = CaseFormat.UPPER_CAMEL; break; case "upper_underscore": this.metaNameCaseFormat = CaseFormat.UPPER_UNDERSCORE; break; default: this.metaNameCaseFormat = null; } this.excerptOutputField = props.getProperty("excerptOutputField", "summary").toLowerCase().trim(); this.stopOnLostMessage = props.getProperty("stopOnLostMessage", "false").equalsIgnoreCase("true"); this.logReplicationMessage = props.getProperty("logReplicationMessage", "false") .equalsIgnoreCase("true"); this.originId = props.getProperty("originId", ""); Properties dusterProps = new InitUtil("duster.", props, false).getProperties(); if (dusterProps.size() > 0) { this.httpClient = new JettyClient(); this.httpClient.init("http.", props, logger); this.dusterClient = new DusterClient(dusterProps, httpClient, logger); logger.info("Duster is enabled..."); } else { logger.info("Duster is disabled..."); } if (!props.getProperty("contentTransformer", "").trim().isEmpty()) { this.contentTransformer = (ContentTransformer) (Class .forName(props.getProperty("contentTransformer")).newInstance()); this.contentTransformer.init(props); } else if (props.getProperty("cleanShortcodes", "true").equalsIgnoreCase("true")) { this.contentTransformer = new ShortcodeCleaner(); } if (!props.getProperty("postTransformer", "").trim().isEmpty()) { this.postTransformer = (PostTransformer) (Class.forName(props.getProperty("postTransformer")) .newInstance()); this.postTransformer.init(props); } else { this.postTransformer = null; } if (!props.getProperty("postFilter", "").trim().isEmpty()) { this.postFilter = (PostFilter) (Class.forName(props.getProperty("postFilter")).newInstance()); this.postFilter.init(props); } else { this.postFilter = null; } String dbDir = props.getProperty("metadb.dir", "").trim(); if (dbDir.isEmpty()) { this.metaDB = null; } else { logger.info(String.format("Initializing meta db, '%s'...", dbDir)); File metaDir = new File(dbDir); if (!metaDir.exists()) { throw new Exception(String.format("The 'metadb.dir' must exist ('%s')", dbDir)); } this.metaDB = new PostMetaDB(metaDir); } Long modifiedOffsetHours = Longs.tryParse(props.getProperty("modifiedOffsetHours", "0")); this.modifiedOffsetMillis = modifiedOffsetHours != null ? modifiedOffsetHours * 3600 * 1000L : 0L; if (this.modifiedOffsetMillis != 0L) { logger.info(String.format("Set modified select offset to %d millis", this.modifiedOffsetMillis)); } this.replicateScheduledState = props.getProperty("replicateScheduledState", "true") .equalsIgnoreCase("true"); this.replicatePendingState = props.getProperty("replicatePendingState", "false") .equalsIgnoreCase("true"); logger.info("Initialized WP supplier..."); } }
From source file:com.cinchapi.concourse.shell.ConcourseShell.java
/** * Attempt to return the {@link #ACCESSIBLE_API_METHODS API method} that is * the closest match for the specified {@code alias}. * <p>/*from w w w. j a va2s . c om*/ * This method can be used to take a user supplied method name that does not * match any of the {@link #ACCESSIBLE_API_METHODS provided} ones, but can * be reasonably assumed to be a valid alias of some sort (i.e. an API * method name in underscore case as opposed to camel case). * </p> * * @param alias the method name that may be an alias for one of the provided * API methods * @return the actual API method that {@code alias} should resolve to, if it * is possible to determine that; otherwise {@code null} */ @Nullable private static String tryGetCorrectApiMethod(String alias) { String camel = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, alias); String expanded = com.cinchapi.concourse.util.Strings.ensureStartsWith(camel, "concourse."); return methods.contains(expanded) && !camel.equals(alias) ? camel : null; }
From source file:dagger2.internal.codegen.SourceFiles.java
private static String factoryPrefix(ProvisionBinding binding) { switch (binding.bindingKind()) { case INJECTION: return ""; case PROVISION: return CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL, ((ExecutableElement) binding.bindingElement()).getSimpleName().toString()); default:/*from w ww .j a v a 2 s. co m*/ throw new IllegalArgumentException(); } }
From source file:com.google.template.soy.shared.internal.AbstractGenerateSoyEscapingDirectiveCode.java
/** * Appends Code to the given buffer./*from w w w . j a v a 2 s. c o m*/ * * <p> * The output Code contains symbol definitions in the appropriate scope (the soy namespace in * JavaScript or a module in Python). * <pre> * ...ESCAPES_FOR_ESCAPE_HTML_ = { ... } // Maps of characters to escaped versions * ...MATCHER_FOR_ESCAPE_HTML_ = <regex> // A single character matching RegExp * ...REPLACER_FOR_ESCAPE_HTML_ = <function> // Usable with replace functions * ...FILTER_FOR_ESCAPE_HTML_ = <regex> // Optional regular expression that vets values. * // A function that uses the above definitions. * ...escapeHtmlHelper = <function> * </pre> * * <p>There is not necessarily a one-to-one relationship between any of the symbols above * and escape directives except for the {@code ...escape...Helper} function. * * @param availableIdentifiers Determines whether a qualified identifier, like * {@code goog.foo.Bar}, is available. * @param outputCode Receives output code. */ @VisibleForTesting void generateCode(Predicate<String> availableIdentifiers, StringBuilder outputCode) { outputCode.append(GENERATED_CODE_START_MARKER).append('\n'); // Before entering the real logic, generate any needed prefix. generatePrefix(outputCode); // First we collect all the side tables. // Like { '\n': '\\n', ... } that map characters to escape. List<Map<Character, String>> escapeMaps = Lists.newArrayList(); // Mangled directive names corresponding to escapeMaps used to generate <namespace>..._ names. List<String> escapeMapNames = Lists.newArrayList(); // Like /[\n\r'"]/g or r'[\n\r\'"]'that match all the characters that need escaping. List<String> matchers = Lists.newArrayList(); // Mangled directive names corresponding to matchers. List<String> matcherNames = Lists.newArrayList(); // RegExps that vet input values. List<String> filters = Lists.newArrayList(); // Mangled directive names corresponding to filters. List<String> filterNames = Lists.newArrayList(); // Bundles of directiveNames and indices into escapeMaps, matchers, etc. List<DirectiveDigest> digests = Lists.newArrayList(); escaperLoop: for (EscapingConventions.CrossLanguageStringXform escaper : EscapingConventions .getAllEscapers()) { // "|escapeHtml" -> "escapeHtml" String escapeDirectiveIdent = escaper.getDirectiveName().substring(1); // "escapeHtml" -> "ESCAPE_HTML" String escapeDirectiveUIdent = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, escapeDirectiveIdent); // If there is an existing function, use it. for (String existingFunction : escaper.getLangFunctionNames(getLanguage())) { if (availableIdentifiers.apply(existingFunction)) { useExistingLibraryFunction(outputCode, escapeDirectiveIdent, existingFunction); continue escaperLoop; } } // Else generate definitions for side tables. int escapesVar = -1; int matcherVar = -1; if (!escaper.getEscapes().isEmpty()) { Map<Character, String> escapeMap = Maps.newTreeMap(); StringBuilder matcherRegexBuf = new StringBuilder(getRegexStart() + "["); int lastCodeUnit = Integer.MIN_VALUE; int rangeStart = Integer.MIN_VALUE; for (EscapingConventions.Escape esc : escaper.getEscapes()) { char ch = esc.getPlainText(); if (ch == lastCodeUnit) { throw new IllegalStateException( "Ambiguous escape " + esc.getEscaped() + " for " + escapeDirectiveIdent); } escapeMap.put(ch, esc.getEscaped()); if (ch != lastCodeUnit + 1) { if (rangeStart != Integer.MIN_VALUE) { escapeRegexpRangeOnto((char) rangeStart, (char) lastCodeUnit, matcherRegexBuf); } rangeStart = ch; } lastCodeUnit = ch; } if (rangeStart < 0) { throw new IllegalStateException(); } escapeRegexpRangeOnto((char) rangeStart, (char) lastCodeUnit, matcherRegexBuf); matcherRegexBuf.append("]").append(getRegexEnd()); // See if we can reuse an existing map. int numEscapeMaps = escapeMaps.size(); for (int i = 0; i < numEscapeMaps; ++i) { if (mapsHaveCompatibleOverlap(escapeMaps.get(i), escapeMap)) { escapesVar = i; break; } } if (escapesVar == -1) { escapesVar = numEscapeMaps; escapeMaps.add(escapeMap); escapeMapNames.add(escapeDirectiveUIdent); } else { escapeMaps.get(escapesVar).putAll(escapeMap); // ESCAPE_JS -> ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX escapeMapNames.set(escapesVar, escapeMapNames.get(escapesVar) + "__AND__" + escapeDirectiveUIdent); } String matcherRegex = matcherRegexBuf.toString(); matcherVar = matchers.indexOf(matcherRegex); if (matcherVar < 0) { matcherVar = matchers.size(); matchers.add(matcherRegex); matcherNames.add(escapeDirectiveUIdent); } else { matcherNames.set(matcherVar, matcherNames.get(matcherVar) + "__AND__" + escapeDirectiveUIdent); } } // Find a suitable filter or add one to filters. int filterVar = -1; Pattern filterPatternJava = escaper.getValueFilter(); if (filterPatternJava != null) { // This is an approximate translation from Java patterns to JavaScript patterns. String filterPattern = convertFromJavaRegex(filterPatternJava); filterVar = filters.indexOf(filterPattern); if (filterVar == -1) { filterVar = filters.size(); filters.add(filterPattern); filterNames.add(escapeDirectiveUIdent); } else { filterNames.set(filterVar, filterNames.get(filterVar) + "__AND__" + escapeDirectiveUIdent); } } digests.add(new DirectiveDigest(escapeDirectiveIdent, escapesVar, matcherVar, filterVar, escaper.getNonAsciiPrefix(), escaper.getInnocuousOutput())); } // TODO(user): Maybe use java Soy templates to generate the JS? // Output the tables. for (int i = 0; i < escapeMaps.size(); ++i) { Map<Character, String> escapeMap = escapeMaps.get(i); String escapeMapName = escapeMapNames.get(i); generateCharacterMapSignature(outputCode, escapeMapName); outputCode.append(" = {"); boolean needsComma = false; for (Map.Entry<Character, String> e : escapeMap.entrySet()) { if (needsComma) { outputCode.append(','); } outputCode.append("\n "); writeUnsafeStringLiteral(e.getKey(), outputCode); outputCode.append(": "); writeStringLiteral(e.getValue(), outputCode); needsComma = true; } outputCode.append("\n}").append(getLineEndSyntax()).append("\n"); generateReplacerFunction(outputCode, escapeMapName); } for (int i = 0; i < matchers.size(); ++i) { String matcherName = matcherNames.get(i); String matcher = matchers.get(i); generateMatcher(outputCode, matcherName, matcher); } for (int i = 0; i < filters.size(); ++i) { String filterName = filterNames.get(i); String filter = filters.get(i); generateFilter(outputCode, filterName, filter); } // Finally, define the helper functions that use the escapes, filters, matchers, etc. for (DirectiveDigest digest : digests) { digest.updateNames(escapeMapNames, matcherNames, filterNames); generateHelperFunction(outputCode, digest); } // Emit patterns and constants needed by escaping functions that are not part of any one // escaping convention. generateCommonConstants(outputCode); outputCode.append('\n').append(GENERATED_CODE_END_MARKER).append('\n'); }
From source file:dagger.internal.codegen.MembersInjectorGenerator.java
private static String injectionSiteDelegateMethodName(Element injectionSiteElement) { return "inject" + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, injectionSiteElement.getSimpleName().toString()); }
From source file:dagger2.internal.codegen.SourceFiles.java
private static String factoryPrefix(ProductionBinding binding) { switch (binding.bindingKind()) { case IMMEDIATE: case FUTURE_PRODUCTION: return CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL, ((ExecutableElement) binding.bindingElement()).getSimpleName().toString()); default:/*from w w w. j a va2s .c o m*/ throw new IllegalArgumentException(); } }
From source file:brooklyn.util.flags.TypeCoercions.java
/** * Type coercion {@link Function function} for {@link Enum enums}. * <p>/*from ww w . j a v a 2 s .co m*/ * Tries to convert the string to {@link CaseFormat#UPPER_UNDERSCORE} first, * handling all of the different {@link CaseFormat format} possibilites. Failing * that, it tries a case-insensitive comparison with the valid enum values. * <p> * Returns {@code defaultValue} if the string cannot be converted. * * @see TypeCoercions#coerce(Object, Class) * @see Enum#valueOf(Class, String) */ public static <E extends Enum<E>> Function<String, E> stringToEnum(final Class<E> type, @Nullable final E defaultValue) { return new Function<String, E>() { @Override public E apply(String input) { Preconditions.checkNotNull(input, "input"); List<String> options = ImmutableList.of(input, CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, input), CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, input), CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, input), CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, input)); for (String value : options) { try { return Enum.valueOf(type, value); } catch (IllegalArgumentException iae) { continue; } } Maybe<E> result = Enums.valueOfIgnoreCase(type, input); return (result.isPresent()) ? result.get() : defaultValue; } }; }
From source file:org.immutables.value.processor.meta.ValueType.java
public String getDocumentName() { Optional<RepositoryMirror> repositoryAnnotation = RepositoryMirror.find(element); if (repositoryAnnotation.isPresent()) { String value = repositoryAnnotation.get().value(); if (!value.isEmpty()) { return value; }// www.j ava 2 s . c o m } return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, name()); }
From source file:org.jclouds.virtualbox.functions.MastersLoadingCache.java
private String getOsTypeId(String os_family, boolean os_64bit) { String osFamily = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, os_family); return os_64bit ? osFamily + "_64" : osFamily; }
From source file:com.google.template.soy.jssrc.internal.GenerateSoyUtilsEscapingDirectiveCode.java
/** * Appends JavaScript to the given buffer. * * <p>//from www. j a v a2s . c om * The output JavaScript contains symbol definitions in the soy namespace. * <pre> * soy.esc.$$ESCAPES_FOR_ESCAPE_HTML_ = { ... }; // Maps of characters to escaped versions * soy.esc.$$MATCHER_FOR_ESCAPE_HTML_ = /.../g; // A single character matching RegExp * soy.esc.$$REPLACER_FOR_ESCAPE_HTML_ = function(ch) { ... }; // Usable with String.replace * soy.esc.$$FILTER_FOR_ESCAPE_HTML_ = /.../g; // Optional regular expression that vets values. * // A function that uses the above definitions. * soy.esc.$$escapeHtmlHelper = function(value) { return ...; }; * </pre> * These definitions are all marked {@code @private} and have Closure compiler type * annotations. * * <p>There is not necessarily a one-to-one relationship between any of the symbols above * and escape directives except for the {@code soy.esc.$$escape...Helper} function. * * @param availableJavaScript Determines whether a qualified JavaScript identifier, like * {@code goog.foo.Bar}, is available. * @param outputJs Receives output JavaScript. */ @VisibleForTesting void generateJavaScript(Predicate<String> availableJavaScript, StringBuilder outputJs) { /** * The JS identifiers associated with the support for a particular escaping directive. */ class DirectiveDigest { /** The name of the directive to output. */ final String directiveName; /** Index into escapes of the object that maps characters to escaped text. */ final int escapesVar; /** Index into matchers. */ final int matcherVar; /** Index into filters. */ final int filterVar; /** The prefix to use for non-ASCII characters not in the escape map. */ final @Nullable String nonAsciiPrefix; /** Innocuous output for this context. */ final String innocuousOutput; DirectiveDigest(String directiveName, int escapesVar, int matcherVar, int filterVar, @Nullable String nonAsciiPrefix, String innocuousOutput) { this.directiveName = directiveName; this.escapesVar = escapesVar; this.matcherVar = matcherVar; this.filterVar = filterVar; this.nonAsciiPrefix = nonAsciiPrefix; this.innocuousOutput = innocuousOutput; } } outputJs.append('\n').append(GENERATED_CODE_START_MARKER).append('\n'); // First we collect all the side tables. // Like { '\n': '\\n', ... } that map characters to escape. List<Map<Character, String>> escapeMaps = Lists.newArrayList(); // Mangled directive names corresponding to escapeMaps used to generate soy.esc.$$..._ names. List<String> escapeMapNames = Lists.newArrayList(); // Like /[\n\r'"]/g that match all the characters that need escaping. List<String> matchers = Lists.newArrayList(); // Mangled directive names corresponding to matchers. List<String> matcherNames = Lists.newArrayList(); // RegExps that vet input values. List<String> filters = Lists.newArrayList(); // Mangled directive names corresponding to filters. List<String> filterNames = Lists.newArrayList(); // Bundles of directiveNames and indices into escapeMaps, matchers, etc. List<DirectiveDigest> digests = Lists.newArrayList(); escaperLoop: for (EscapingConventions.CrossLanguageStringXform escaper : EscapingConventions .getAllEscapers()) { // "|escapeHtml" -> "escapeHtml" String escapeDirectiveIdent = escaper.getDirectiveName().substring(1); // "escapeHtml" -> "ESCAPE_HTML" String escapeDirectiveUIdent = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, escapeDirectiveIdent); // If there is an existing function, use it. for (String existingFunction : escaper.getJsFunctionNames()) { if (availableJavaScript.apply(existingFunction)) { // soy.esc.$$escapeFooHelper = bar; outputJs.append('\n').append("/**\n").append(" * @type {function (*) : string}\n") .append(" */\n").append("soy.esc.$$").append(escapeDirectiveIdent) .append("Helper = function(v) {\n").append(" return ").append(existingFunction) .append("(String(v));\n").append("};\n"); continue escaperLoop; } } // Else generate definitions for side tables. int escapesVar = -1; int matcherVar = -1; if (!escaper.getEscapes().isEmpty()) { Map<Character, String> escapeMap = Maps.newLinkedHashMap(); StringBuilder matcherRegexBuf = new StringBuilder("/["); int lastCodeUnit = Integer.MIN_VALUE; int rangeStart = Integer.MIN_VALUE; for (EscapingConventions.Escape esc : escaper.getEscapes()) { char ch = esc.getPlainText(); if (ch == lastCodeUnit) { throw new IllegalStateException( "Ambiguous escape " + esc.getEscaped() + " for " + escapeDirectiveIdent); } escapeMap.put(ch, esc.getEscaped()); if (ch != lastCodeUnit + 1) { if (rangeStart != Integer.MIN_VALUE) { escapeRegexpRangeOnto((char) rangeStart, (char) lastCodeUnit, matcherRegexBuf); } rangeStart = ch; } lastCodeUnit = ch; } if (rangeStart < 0) { throw new IllegalStateException(); } escapeRegexpRangeOnto((char) rangeStart, (char) lastCodeUnit, matcherRegexBuf); matcherRegexBuf.append("]/g"); // See if we can reuse an existing map. int numEscapeMaps = escapeMaps.size(); for (int i = 0; i < numEscapeMaps; ++i) { if (mapsHaveCompatibleOverlap(escapeMaps.get(i), escapeMap)) { escapesVar = i; break; } } if (escapesVar == -1) { escapesVar = numEscapeMaps; escapeMaps.add(escapeMap); escapeMapNames.add(escapeDirectiveUIdent); } else { escapeMaps.get(escapesVar).putAll(escapeMap); // ESCAPE_JS -> ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX escapeMapNames.set(escapesVar, escapeMapNames.get(escapesVar) + "__AND__" + escapeDirectiveUIdent); } String matcherRegex = matcherRegexBuf.toString(); matcherVar = matchers.indexOf(matcherRegex); if (matcherVar < 0) { matcherVar = matchers.size(); matchers.add(matcherRegex); matcherNames.add(escapeDirectiveUIdent); } else { matcherNames.set(matcherVar, matcherNames.get(matcherVar) + "__AND__" + escapeDirectiveUIdent); } } // Find a suitable filter or add one to filters. int filterVar = -1; Pattern filterPatternJava = escaper.getValueFilter(); if (filterPatternJava != null) { // This is an approximate translation from Java patterns to JavaScript patterns. String filterPattern = javaRegexToJs(filterPatternJava); filterVar = filters.indexOf(filterPattern); if (filterVar == -1) { filterVar = filters.size(); filters.add(filterPattern); filterNames.add(escapeDirectiveUIdent); } else { filterNames.set(filterVar, filterNames.get(filterVar) + "__AND__" + escapeDirectiveUIdent); } } digests.add(new DirectiveDigest(escapeDirectiveIdent, escapesVar, matcherVar, filterVar, escaper.getNonAsciiPrefix(), escaper.getInnocuousOutput())); } // TODO: Maybe use java Soy templates to generate the JS? // Output the tables. for (int i = 0; i < escapeMaps.size(); ++i) { Map<Character, String> escapeMap = escapeMaps.get(i); String escapeMapName = escapeMapNames.get(i); outputJs.append('\n').append("/**\n") .append(" * Maps charcters to the escaped versions for the named escape directives.\n") .append(" * @type {Object.<string, string>}\n").append(" * @private\n").append(" */\n") .append("soy.esc.$$ESCAPE_MAP_FOR_").append(escapeMapName).append("_ = {"); boolean needsComma = false; for (Map.Entry<Character, String> e : escapeMap.entrySet()) { if (needsComma) { outputJs.append(','); } outputJs.append("\n "); writeJsChar(e.getKey(), outputJs); outputJs.append(": "); writeJsString(e.getValue(), outputJs); needsComma = true; } outputJs.append("\n};\n"); outputJs.append('\n').append("/**\n").append(" * A function that can be used with String.replace..\n") .append(" * @param {string} ch A single character matched by a compatible matcher.\n") .append(" * @return {string} A token in the output language.\n").append(" * @private\n") .append(" */\n").append("soy.esc.$$REPLACER_FOR_").append(escapeMapName) .append("_ = function(ch) {\n").append(" return soy.esc.$$ESCAPE_MAP_FOR_") .append(escapeMapName).append("_[ch];\n").append("};\n"); } for (int i = 0; i < matchers.size(); ++i) { String matcher = matchers.get(i); String matcherName = matcherNames.get(i); outputJs.append('\n').append("/**\n") .append(" * Matches characters that need to be escaped for the named directives.\n") .append(" * @type RegExp\n").append(" * @private\n").append(" */\n") .append("soy.esc.$$MATCHER_FOR_").append(matcherName).append("_ = ").append(matcher) .append(";\n"); } for (int i = 0; i < filters.size(); ++i) { String filter = filters.get(i); String filterName = filterNames.get(i); outputJs.append('\n').append("/**\n") .append(" * A pattern that vets values produced by the named directives.\n") .append(" * @type RegExp\n").append(" * @private\n").append(" */\n") .append("soy.esc.$$FILTER_FOR_").append(filterName).append("_ = ").append(filter).append(";\n"); } // Finally, define the helper functions that use the escapes, filters, matchers, etc. for (DirectiveDigest digest : digests) { String name = digest.directiveName; outputJs.append('\n').append("/**\n").append(" * A helper for the Soy directive |").append(name) .append('\n') .append(" * @param {*} value Can be of any type but will be coerced to a string.\n") .append(" * @return {string} The escaped text.\n").append(" */\n").append("soy.esc.$$") .append(name).append("Helper = function(value) {\n").append(" var str = String(value);\n"); if (digest.filterVar != -1) { String filterName = filterNames.get(digest.filterVar); outputJs.append(" if (!soy.esc.$$FILTER_FOR_").append(filterName).append("_.test(str)) {\n"); if (availableJavaScript.apply("goog.asserts.fail")) { outputJs.append(" goog.asserts.fail('Bad value `%s` for |").append(name) .append("', [str]);\n"); } outputJs.append(" return '").append(digest.innocuousOutput).append("';\n").append(" }\n"); } if (digest.nonAsciiPrefix != null) { // TODO: We can add a second replace of all non-ascii codepoints below. throw new UnsupportedOperationException("Non ASCII prefix escapers not implemented yet."); } if (digest.escapesVar >= 0) { String escapeMapName = escapeMapNames.get(digest.escapesVar); String matcherName = matcherNames.get(digest.matcherVar); outputJs.append(" return str.replace(\n").append(" soy.esc.$$MATCHER_FOR_") .append(matcherName).append("_,\n").append(" soy.esc.$$REPLACER_FOR_") .append(escapeMapName).append("_);\n"); } else { outputJs.append(" return str;\n"); } outputJs.append("};\n"); } // Emit patterns and constants needed by escaping functions that are not part of any one // escaping convention. outputJs.append('\n').append("/**\n") .append(" * Matches all tags, HTML comments, and DOCTYPEs in tag soup HTML.\n") .append(" * By removing these, and replacing any '<' or '>' characters with\n") .append(" * entities we guarantee that the result can be embedded into a\n") .append(" * an attribute without introducing a tag boundary.\n").append(" *\n") .append(" * @type {RegExp}\n").append(" * @private\n").append(" */\n") .append("soy.esc.$$HTML_TAG_REGEX_ = ").append(javaRegexToJs(EscapingConventions.HTML_TAG_CONTENT)) .append("g;\n").append("\n").append("/**\n").append(" * Matches all occurrences of '<'.\n") .append(" *\n").append(" * @type {RegExp}\n").append(" * @private\n").append(" */\n") .append("soy.esc.$$LT_REGEX_ = /</g;\n"); outputJs.append('\n').append("/**\n").append(" * Maps lower-case names of innocuous tags to 1.\n") .append(" *\n").append(" * @type {Object.<string,number>}\n").append(" * @private\n") .append(" */\n").append("soy.esc.$$SAFE_TAG_WHITELIST_ = ") .append(toJsStringSet(TagWhitelist.FORMATTING.asSet())).append(";\n"); outputJs.append('\n').append(GENERATED_CODE_END_MARKER).append('\n'); }