All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2009 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.gwt.uibinder.attributeparsers;

import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.uibinder.rebind.FieldManager;
import com.google.gwt.uibinder.rebind.XMLElement;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Deals with field references, e.g. the bits in braces here: <div
 * class="{style.enabled} fancy {style.impressive}" />, by converting
 * them to java expressions (with the help of a
 * {@link com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter.Delegate
 * Delegate}).
 * 

* A field reference is one or more segments separated by dots. The first * segment is considered to be a reference to a ui field, and succeeding * segments are method calls. So, "{able.baker.charlie}" becomes * "able.baker().charlie()". *

* A field reference starts with '{' and is followed immediately by a character * that can legally start a java identifier—that is a letter, $, or * underscore. Braces not followed by such a character are left in place. *

* For convenience when dealing with generated CssResources, field segments with * dashes are converted to camel case. That is, {able.baker-charlie} is the same * as {able.bakerCharlie} *

* Double mustaches (i.e. "{{..}}") are not matched as references to play well * with modern templating systems. *

* Opening braces may be escape by slash. That is, "\{foo}" will converted to * "{foo}", with no field reference detected. */ public class FieldReferenceConverter { /** * May be thrown by the * {@link com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter.Delegate * Delegate} for badly formatted input. */ @SuppressWarnings("serial") public static class IllegalFieldReferenceException extends RuntimeException { } /** * Responsible for the bits around and between the field references. May throw * IllegalFieldReferenceException as it sees fit. */ interface Delegate { /** * Returns the types any parsed field references are expected to return. * Multiple values indicates an overload. E.g., in either a * String or a SafeUri is allowed. */ JType[] getTypes(); /** * Called for fragment around and between field references. *

* Note that it will be called with empty strings if these surrounding bits * are empty. E.g., "{style.enabled} fancy {style.impressive}" would call * this method three times, with "", " fancy ", and "". *

* A string with no field references is treated as a single fragment, and * causes a single call to this method. */ String handleFragment(String fragment) throws IllegalFieldReferenceException; /** * Called for each expanded field reference, to allow it to be stitched * together with surrounding fragments. */ String handleReference(String reference) throws IllegalFieldReferenceException; } /** * Used by {@link FieldReferenceConverter#countFieldReferences}. Passthrough * implementation that counts the number of calls handleReference has been called, * so that we know how many field references a given string contains. */ private static final class Telltale implements FieldReferenceConverter.Delegate { private int computedCount; public int getComputedCount() { return computedCount; } public JType[] getTypes() { return new JType[0]; } public String handleFragment(String fragment) { return fragment; } public String handleReference(String reference) { computedCount++; return reference; } } private static final Pattern BRACES = Pattern.compile("[{]([^}]*)[}]"); private static final Pattern LEGAL_FIRST_CHAR = Pattern.compile("^[$_a-zA-Z].*"); private static final String DOTS_AND_PARENS = "[().]"; /** * Returns the number of field references in the given string. */ public static int countFieldReferences(String string) { Telltale telltale = new Telltale(); new FieldReferenceConverter(null).convert(null, string, telltale); return telltale.getComputedCount(); } /** * Reverses most of the work of {@link #convert}, turning a java expression * back into a dotted path. */ public static String expressionToPath(String expression) { String[] chunks = expression.split(DOTS_AND_PARENS); StringBuilder b = new StringBuilder(); for (String chunk : chunks) { if (b.length() > 0 && chunk.length() > 0) { b.append("."); } b.append(chunk); } return b.toString(); } /** * Returns true if the given string holds one or more field references. */ public static boolean hasFieldReferences(String string) { return countFieldReferences(string) > 0; } private final CssNameConverter cssConverter = new CssNameConverter(); private final FieldManager fieldManager; /** * @param fieldManager to register parsed references with. May be null */ FieldReferenceConverter(FieldManager fieldManager) { this.fieldManager = fieldManager; } /** * @throws IllegalFieldReferenceException if the delegate does */ public String convert(String in, Delegate delegate) { return convert(null, in, delegate); } /** * @throws IllegalFieldReferenceException if the delegate does */ public String convert(XMLElement source, String in, Delegate delegate) { StringBuilder b = new StringBuilder(); int nextFindStart = 0; int lastMatchEnd = 0; Matcher m = BRACES.matcher(in); while (m.find(nextFindStart)) { String fieldReference = m.group(1); int start = m.start(); if (!isLegalPreviousCharacter(in, start)) { nextFindStart = start + 1; continue; } if (!isLegalFirstCharacter(fieldReference)) { nextFindStart = start + 2; continue; } String precedingFragment = in.substring(lastMatchEnd, start); precedingFragment = handleFragment(precedingFragment, delegate); b.append(precedingFragment); if (fieldManager != null) { fieldManager.registerFieldReference(source, fieldReference, delegate.getTypes()); } fieldReference = expandDots(fieldReference); b.append(delegate.handleReference(fieldReference)); nextFindStart = lastMatchEnd = m.end(); } b.append(handleFragment(in.substring(lastMatchEnd), delegate)); return b.toString(); } private String expandDots(String value) { StringBuilder b = new StringBuilder(); String[] segments = value.split("[.]"); for (int i = 0; i < segments.length; ++i) { String segment = cssConverter.convertName(segments[i]); // The first segment is converted to a field getter. So, // "bundle.whatever" becomes "get_bundle().whatever". if (fieldManager != null && i == 0) { segment = fieldManager.convertFieldToGetter(segment); } if (b.length() == 0) { b.append(segment); // field name } else { b.append(".").append(segment).append("()"); } } return b.toString(); } private String handleFragment(String fragment, Delegate delegate) { fragment = fragment.replace("\\{", "{"); return delegate.handleFragment(fragment); } private boolean isLegalFirstCharacter(String fieldReference) { return LEGAL_FIRST_CHAR.matcher(fieldReference).matches(); } private boolean isLegalPreviousCharacter(String in, int start) { if (start < 1) { return true; } char previousChar = in.charAt(start - 1); return previousChar != '{' && previousChar != '\\'; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy