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

org.eclipse.jface.text.templates.TemplateTranslator Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jface.text.templates;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * The template translator translates a string into a template buffer. Regions marked as variables
 * are translated into TemplateVariables.
 * 

* The EBNF grammar of a valid string is as follows: *

*
 template := (text | escape)*.
 * text := character - dollar.
 * escape := dollar ('{' variable '}' | dollar).
 * dollar := '$'.
 * variable := identifier | identifier ':' type.
 * type := qualifiedname | qualifiedname '(' arguments ')'.
 * arguments := (argument ',')* argument.
 * argument := qualifiedname | argumenttext.
 * qualifiedname := (identifier '.')* identifier.
 * argumenttext := "'" (character - "'" | "'" "'")* "'".
 * identifier := javaidentifierpart - "$".
*

* Clients may only replace the createVariable method of this class. *

* * @since 3.0 */ public class TemplateTranslator { /** * Regex pattern for identifier. * Note: For historic reasons, this pattern allows numbers at the beginning of an identifier. * @since 3.7 */ private static final String IDENTIFIER= "(?:[\\p{javaJavaIdentifierPart}&&[^\\$]]++)"; //$NON-NLS-1$ /** * Regex pattern for qualifiedname * @since 3.4 */ private static final String QUALIFIED_NAME= "(?:" + IDENTIFIER + "\\.)*+" + IDENTIFIER; //$NON-NLS-1$ //$NON-NLS-2$ /** * Regex pattern for argumenttext * @since 3.4 */ private static final String ARGUMENT_TEXT= "'(?:(?:'')|(?:[^']))*+'"; //$NON-NLS-1$ /** * Regex pattern for argument * @since 3.4 */ private static final String ARGUMENT= "(?:" + QUALIFIED_NAME + ")|(?:" + ARGUMENT_TEXT + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ /** * Regex pattern for whitespace * @since 3.5 */ private static final String SPACES= "\\s*+"; //$NON-NLS-1$ /** * Precompiled regex pattern for qualified names. * @since 3.3 */ private static final Pattern PARAM_PATTERN= Pattern.compile(ARGUMENT); /** * Precompiled regex pattern for valid dollar escapes (dollar literals and variables) and * (invalid) single dollars. * @since 3.3 */ private static final Pattern ESCAPE_PATTERN= Pattern.compile( "\\$\\$|\\$\\{" + // $$|${ //$NON-NLS-1$ SPACES + "(" + IDENTIFIER + "?+)" + // variable id group (1) //$NON-NLS-1$ //$NON-NLS-2$ SPACES + "(?:" + //$NON-NLS-1$ ":" + //$NON-NLS-1$ SPACES + "(" + QUALIFIED_NAME + ")" + // variable type group (2) //$NON-NLS-1$ //$NON-NLS-2$ SPACES + "(?:" + //$NON-NLS-1$ "\\(" + // ( //$NON-NLS-1$ SPACES + "((?:(?:" + ARGUMENT + ")" + SPACES + "," + SPACES + ")*+(?:" + ARGUMENT + "))" + // arguments group (3) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ SPACES + "\\)" + // ) //$NON-NLS-1$ ")?" + //$NON-NLS-1$ SPACES + ")?" + //$NON-NLS-1$ "\\}|\\$"); // }|$ //$NON-NLS-1$ /** * @since 3.3 */ private final class VariableDescription { final List fOffsets= new ArrayList<>(5); final String fName; TemplateVariableType fType; VariableDescription(String name, TemplateVariableType type) { fName= name; fType= type; } void mergeType(TemplateVariableType type) throws TemplateException { if (type == null) return; if (fType == null) fType= type; if (!type.equals(fType)) fail(TextTemplateMessages.getFormattedString("TemplateTranslator.error.incompatible.type", fName)); //$NON-NLS-1$ } } /** Last translation error. */ private String fErrorMessage; /** * Used to ensure compatibility with subclasses overriding * {@link #createVariable(String, String, int[])}. * @since 3.3 */ private TemplateVariableType fCurrentType; /** * Returns an error message if an error occurred for the last translation, null * otherwise. * * @return the error message if an error occurred during the most recent translation, * null otherwise */ public String getErrorMessage() { return fErrorMessage; } /** * Translates a template to a TemplateBuffer. null is returned if * there was an error. getErrorMessage() retrieves the associated error message. * * @param template the template to translate. * @return returns the template buffer corresponding to the string * @see #getErrorMessage() * @throws TemplateException if translation failed */ public TemplateBuffer translate(Template template) throws TemplateException { return parse(template.getPattern()); } /** * Translates a template string to TemplateBuffer. null is * returned if there was an error. getErrorMessage() retrieves the associated * error message. * * @param string the string to translate. * @return returns the template buffer corresponding to the string * @see #getErrorMessage() * @throws TemplateException if translation failed */ public TemplateBuffer translate(String string) throws TemplateException { return parse(string); } /** * Internal parser. * * @param string the string to parse * @return the parsed TemplateBuffer * @throws TemplateException if the string does not conform to the template format */ private TemplateBuffer parse(String string) throws TemplateException { fErrorMessage= null; final StringBuilder buffer= new StringBuilder(string.length()); final Matcher matcher= ESCAPE_PATTERN.matcher(string); final Map variables= new LinkedHashMap<>(); int complete= 0; while (matcher.find()) { // append any verbatim text buffer.append(string.substring(complete, matcher.start())); // check the escaped sequence switch (matcher.group()) { case "$": //$NON-NLS-1$ fail(TextTemplateMessages.getString("TemplateTranslator.error.incomplete.variable")); //$NON-NLS-1$ break; case "$$": //$NON-NLS-1$ // escaped $ buffer.append('$'); break; default: // parse variable String name= matcher.group(1); String typeName= matcher.group(2); String params= matcher.group(3); TemplateVariableType type= createType(typeName, params); updateOrCreateVariable(variables, name, type, buffer.length()); buffer.append(name); break; } complete= matcher.end(); } // append remaining verbatim text buffer.append(string.substring(complete)); TemplateVariable[] vars= createVariables(variables); fixOffsetsAndBuffer(buffer, vars); return new TemplateBuffer(buffer.toString(), vars); } /** * In cases default value is provided dynamically when instantiating the variable (so not part * of the {@link VariableDescription}), the buffer would actually contain incorrect text (name instead * of default value) and offsets can also be inconsistent if variable name and default value have different * length. This methods fix both buffer and variable offsets to ensure default value is used. * @param buffer the default template string * @param vars variables */ private void fixOffsetsAndBuffer(StringBuilder buffer, TemplateVariable[] vars) { SortedMap varsByOffset = new TreeMap<>(); for (TemplateVariable var : vars) { for (int offset : var.getOffsets()) { varsByOffset.put(Integer.valueOf(offset), var); } } int totalOffsetDelta = 0; Map> fixedOffsets = new HashMap<>(vars.length, 1.f); for (Entry entry : varsByOffset.entrySet()) { final int initialOffset = entry.getKey().intValue(); TemplateVariable variable = entry.getValue(); final int fixedOffset = initialOffset + totalOffsetDelta; fixedOffsets.computeIfAbsent(variable, v -> new ArrayList<>(v.getOffsets().length)).add(Integer.valueOf(fixedOffset)); int currentOffsetDelta = variable.getDefaultValue().length() - variable.getName().length(); buffer.replace(fixedOffset, fixedOffset + variable.getName().length(), variable.getDefaultValue()); totalOffsetDelta += currentOffsetDelta; } fixedOffsets.forEach((variable, fixs) -> variable.setOffsets(fixs.stream().mapToInt(Integer::valueOf).toArray())); } private TemplateVariableType createType(String typeName, String paramString) { if (typeName == null) return null; if (paramString == null) return new TemplateVariableType(typeName); final Matcher matcher= PARAM_PATTERN.matcher(paramString); List params= new ArrayList<>(5); while (matcher.find()) { String argument= matcher.group(); if (argument.charAt(0) == '\'') { // argumentText argument= argument.substring(1, argument.length() - 1).replace("''", "'"); //$NON-NLS-1$ //$NON-NLS-2$ } params.add(argument); } return new TemplateVariableType(typeName, params.toArray(new String[params.size()])); } private void fail(String message) throws TemplateException { fErrorMessage= message; throw new TemplateException(message); } /** * If there is no variable named name, a new variable with the given type, name * and offset is created. If one exists, the offset is added to the variable and the type is * merged with the existing type. * * @param variables the variables by variable name * @param name the name of the variable * @param type the variable type, null for not defined * @param offset the buffer offset of the variable * @return the variable description found or created for that name * @throws TemplateException if merging the type fails * @since 3.3 */ private VariableDescription updateOrCreateVariable(Map variables, String name, TemplateVariableType type, int offset) throws TemplateException { VariableDescription varDesc= variables.get(name); if (varDesc == null) { varDesc= new VariableDescription(name, type); variables.put(name, varDesc); } else { varDesc.mergeType(type); } varDesc.fOffsets.add(Integer.valueOf(offset)); return varDesc; } /** * Creates proper {@link TemplateVariable}s from the variable descriptions. * * @param variables the variable descriptions by variable name * @return the corresponding variables * @since 3.3 */ private TemplateVariable[] createVariables(Map variables) { TemplateVariable[] result= new TemplateVariable[variables.size()]; int idx= 0; for (Iterator it= variables.values().iterator(); it.hasNext(); idx++) { VariableDescription desc= it.next(); TemplateVariableType type= desc.fType == null ? new TemplateVariableType(desc.fName) : desc.fType; int[] offsets= new int[desc.fOffsets.size()]; int i= 0; for (Iterator intIt= desc.fOffsets.iterator(); intIt.hasNext(); i++) { Integer offset= intIt.next(); offsets[i]= offset.intValue(); } fCurrentType= type; /* * Call the deprecated version of createVariable. When not overridden, it will delegate * to the new version using fCurrentType. */ TemplateVariable var= createVariable(type.getName(), desc.fName, offsets); result[idx]= var; } fCurrentType= null; // avoid dangling reference return result; } /** * Hook method to create new variables. Subclasses may override to supply their custom variable * type. *

* Clients may replace this method. *

* * @param type the type of the new variable. * @param name the name of the new variable. * @param offsets the offsets where the variable occurs in the template * @return a new instance of TemplateVariable * @deprecated as of 3.3 use {@link #createVariable(TemplateVariableType, String, int[])} instead */ @Deprecated protected TemplateVariable createVariable(String type, String name, int[] offsets) { return createVariable(fCurrentType, name, offsets); } /** * Hook method to create new variables. Subclasses may override to supply their custom variable * type. *

* Clients may replace this method. *

* * @param type the type of the new variable. * @param name the name of the new variable. * @param offsets the offsets where the variable occurs in the template * @return a new instance of TemplateVariable * @since 3.3 */ protected TemplateVariable createVariable(TemplateVariableType type, String name, int[] offsets) { return new TemplateVariable(type, name, name, offsets); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy