/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.flex.compiler.internal.mxml;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import org.apache.flex.compiler.common.PrefixMap;
import org.apache.flex.compiler.common.XMLName;
import org.apache.flex.compiler.internal.projects.FlexProject;
import org.apache.flex.compiler.mxml.IMXMLLanguageConstants;
/**
* This is the abstract base class for classes representing dialects of MXML,
* such as MXML 2006 or MXML 2009.
* <p>
* Each MXML dialect has a unique language namespace, such as
* <code>"http://ns.adobe.com/mxml/2009"</code> for MXML 2009.
* <p>
* You can use an MXML dialect to get the XML name for various core tags of the
* language, such as <code>("http://ns.adobe.com/mxml/2009", "Library")</code>
* for the {@code <Library>} tag of MXML 2009. The name will be
* <code>null</code> if the tag is not supported in the dialect. For example,
* the {@code <Library>} tag does not exist in MXML 2006.
* <p>
* You can also use an MXML dialect to perform core MXML parsing, such as
* parsing ActionScript values of type <code>Boolean</code>, <code>int</code>,
* etc. from attribute values and character data.
* <p>
* Static methods on this class allow you to determine whether a URI is language
* namespace for a dialect of MXML, and to get the dialect object for that
* namespace.
*/
public abstract class MXMLDialect
{
/**
* The <code>MXMLDialect</code> representing MXML 2012 (experimental).
*/
public static final MXMLDialect MXML_2012 = MXMLDialect2012.getInstance();
/**
* The <code>MXMLDialect</code> representing MXML 2009.
*/
public static final MXMLDialect MXML_2009 = MXMLDialect2009.getInstance();
/**
* The <code>MXMLDialect</code> representing MXML 2006.
*/
public static final MXMLDialect MXML_2006 = MXMLDialect2006.getInstance();
/**
* The <code>MXMLDialect</code> representing the default dialect of MXML. It
* is used when the dialect is not explicitly expressed.
*/
public static final MXMLDialect DEFAULT = MXML_2009;
/*
* A map of language namespace to dialect, such as
* "http://ns.adobe.com/mxml/2009" -> MXMLDialect.MXML_2009.
*/
private static final Map<String, MXMLDialect> DIALECT_MAP =
new ImmutableMap.Builder<String, MXMLDialect>()
.put(MXML_2006.getLanguageNamespace(), MXML_2006)
.put(MXML_2009.getLanguageNamespace(), MXML_2009)
.put(MXML_2012.getLanguageNamespace(), MXML_2012)
.build();
/**
* A map of entities to characters, such as "lt" -> '<'.
* TODO HTML 4 supports about 250 named characters
* HTML 5 supports about 2500. How many should MXML support
* beyond these required 5? What did Xerces/mxmlc support?
*/
private static final Map<String, Character> NAMED_ENTITY_MAP =
new ImmutableMap.Builder<String, Character>()
.put("amp", '&')
.put("apos", '\'')
.put("gt", '>')
.put("lt", '<')
.put("quot", '"')
.build();
/**
* Determines whether a specified URI is the language namespace for a
* supported dialect of MXML.
* <p>
* The supported URIs are:
* <ul>
* <li><code>"http://www.adobe.com/2006/mxml"</code> for MXML 2006</li>
* <li><code>"http://ns.adobe.com/mxml/2009"</code> for MXML 2009</li>
* <li><code>"http://ns.adobe.com/mxml/2012"</code> for MXML 2012</li>
* </ul>
*
* @param uri A URI specifying a language namespace.
* @return <code>true</code> if the URI is a supported language namespace.
*/
public static boolean isLanguageNamespace(String uri)
{
return DIALECT_MAP.containsKey(uri);
}
/**
* Gets the <code>MXMLDialect</code> object corresponding to a specified
* language namespace.
*
* @param uri A URI string specifying a langauge namespace.
* @return An <code>MXMLDialect</code> or <code>null</code>.
*/
public static MXMLDialect getDialectForLanguageNamespace(String uri)
{
return DIALECT_MAP.get(uri);
}
/**
* Given a <code>PrefixMap</code> representing the <code>xmlns</code>
* attributes on the root tag, determines which dialect of MXML is being
* used.
*
* @param rootPrefixMap A {@link PrefixMap}.
* @return An {@link MXMLDialect}.
*/
public static MXMLDialect getMXMLDialect(PrefixMap rootPrefixMap)
{
for (String prefix : rootPrefixMap.getAllPrefixes())
{
String ns = rootPrefixMap.getNamespaceForPrefix(prefix);
if (isLanguageNamespace(ns))
return getDialectForLanguageNamespace(ns);
}
// When we can't find anything, use the default dialect.
return MXMLDialect.DEFAULT;
}
/**
* Constructor.
*
* @param languageNamespace The language namespace URI that identifies this
* dialect of MXML, such as <code>"http://ns.adobe.com/mxml/2009"</code> for
* MXML 2009.
* @param year The numeric value (such as <code>2009</code>) used for
* comparing different dialects of MXML.
*/
protected MXMLDialect(String languageNamespace, int year)
{
this.languageNamespace = languageNamespace;
this.year = year;
// Names of language tags for builtin types.
arrayXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.ARRAY);
booleanXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.BOOLEAN);
classXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.CLASS);
dateXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.DATE);
functionXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.FUNCTION);
intXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.INT);
numberXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.NUMBER);
objectXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.OBJECT);
stringXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.STRING);
uintXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.UINT);
xmlXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.XML);
xmlListXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.XML_LIST);
}
private final String languageNamespace;
private final int year;
// Names of language tags that represent builtin ActionScript types.
private final XMLName arrayXMLName;
private final XMLName booleanXMLName;
private final XMLName classXMLName;
private final XMLName dateXMLName;
private final XMLName functionXMLName;
private final XMLName intXMLName;
private final XMLName numberXMLName;
private final XMLName objectXMLName;
private final XMLName stringXMLName;
private final XMLName uintXMLName;
private final XMLName xmlXMLName;
private final XMLName xmlListXMLName;
// Names of special language tags that don't represent builtiln ActionScript types.
protected XMLName bindingXMLName;
protected XMLName componentXMLName;
protected XMLName declarationsXMLName;
protected XMLName definitionXMLName;
protected XMLName libraryXMLName;
protected XMLName metadataXMLName;
protected XMLName modelXMLName;
protected XMLName privateXMLName;
protected XMLName reparentXMLName;
protected XMLName scriptXMLName;
protected XMLName styleXMLName;
/**
* Gets the language namespace for this dialect of MXML.
* <p>
* For MXML 2009, for example, this is
* <code>"http://ns.adobe.com/mxml/2009"</code>.
*
* @return The language namespace as a String.
*/
public String getLanguageNamespace()
{
return languageNamespace;
}
/**
* Determines whether this dialect is equal to, or later than, another
* dialect.
*
* @param other Another <code>MXMLDialect</code>.
* @return <code>true</code if it.
*/
public boolean isEqualToOrAfter(MXMLDialect other)
{
return year >= other.year;
}
/**
* Determines whether this dialect is equal to, or earlier than, another
* dialect.
*
* @param other Another <code>MXMLDialect</code>.
* @return <code>true</code if it.
*/
public boolean isEqualToOrBefore(MXMLDialect other)
{
return year <= other.year;
}
/**
* Gets the XML name of the {@code <Array>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveArray()
{
return arrayXMLName;
}
/**
* Gets the XML name of the {@code <Binding>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveBinding()
{
return bindingXMLName;
}
/**
* Gets the XML name of the {@code <Boolean>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveBoolean()
{
return booleanXMLName;
}
/**
* Gets the XML name of the {@code <Class>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveClass()
{
return classXMLName;
}
/**
* Gets the XML name of the {@code <Component>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveComponent()
{
return componentXMLName;
}
/**
* Gets the XML name of the {@code <De3clarations>} tag in this dialect of
* MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveDeclarations()
{
return declarationsXMLName;
}
/**
* Gets the XML name of the {@code <Date>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveDate()
{
return dateXMLName;
}
/**
* Gets the XML name of the {@code <Definition>} tag in this dialect of
* MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveDefinition()
{
return definitionXMLName;
}
/**
* Gets the XML name of the {@code <Function>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveFunction()
{
return functionXMLName;
}
/**
* Gets the XML name of the {@code <int>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveInt()
{
return intXMLName;
}
/**
* Gets the XML name of the {@code <Library>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveLibrary()
{
return libraryXMLName;
}
/**
* Gets the XML name of the {@code <Metadata>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveMetadata()
{
return metadataXMLName;
}
/**
* Gets the XML name of the {@code <Model>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveModel()
{
return modelXMLName;
}
/**
* Gets the XML name of the {@code <Number>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveNumber()
{
return numberXMLName;
}
/**
* Gets the XML name of the {@code <Object>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveObject()
{
return objectXMLName;
}
/**
* Gets the XML name of the {@code <Private>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolvePrivate()
{
return privateXMLName;
}
/**
* Gets the XML name of the {@code <Reparent>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveReparent()
{
return reparentXMLName;
}
/**
* Gets the XML name of the {@code <Script>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveScript()
{
return scriptXMLName;
}
/**
* Gets the XML name of the {@code <String>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveString()
{
return stringXMLName;
}
/**
* Gets the XML name of the {@code <Style>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveStyle()
{
return styleXMLName;
}
/**
* Gets the XML name of the {@code <uint>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveUint()
{
return uintXMLName;
}
/**
* Gets the XML name of the {@code <XML>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveXML()
{
return xmlXMLName;
}
/**
* Gets the XML name of the {@code <XMLList>} tag in this dialect of MXML.
*
* @return An {@link XMLName} or <code>null</code> if this tag is not
* supported.
*/
public XMLName resolveXMLList()
{
return xmlListXMLName;
}
/**
* Gets the character corresponding to an entity name in this dialect of
* MXML.
* <p>
* For example, if you pass the entity name <code>"lt"</code>, this returns
* the character <code>'>'</code>.
*
* @return A Character, or <code>null</code> if the entity name is invalid
* in this dialect of MXML.
*/
public Character getNamedEntity(String entityName)
{
return NAMED_ENTITY_MAP.get(entityName);
}
/**
* Determines whether a character is considered whitespace in this dialect
* of MXML.
*
* @param c The character.
* @return <code>true</code> if the character is whitespace.
*/
public abstract boolean isWhitespace(char c);
/**
* Determines whether a text String contains only characters that are
* considered whitespace in this dialect of MXML.
*
* @param s The string.
* @return true if the string is all-whitespace, or empty.
*/
public abstract boolean isWhitespace(String s);
/**
* Removes whitespace from the input string and returns a string that
* contains at most 1 'replacementChar' character between each word.
* <p>
* This method can be used to strip newlines, tabs, multiple spaces, etc.
* between words and replace them with a single space.
*
* @param s The input String.
* @param replacementChar The character that replaces whitespace between
* words.
* @return The output String.
*/
public abstract String collapseWhitespace(String s, char replacementChar);
/**
* Removes any leading and trailing characters from a String that are
* considered whitespace in this version of MXML.
*
* @param s A String.
* @return A new String with the leading and trailing whitespace removed.
*/
public abstract String trim(String s);
/**
* Splits a string at commas and then trims whitespace from each part.
*
* @param s The input string.
* @return An array of trimmed strings split from the input string.
*/
public abstract String[] splitAndTrim(String s);
/**
* Parses an ActionScript <code>Boolean</code> value from a string.
*
* @param project The {@link FlexProject} within which the MXML is being
* parsed.
* @param s The string to be parsed.
* @param flags A set of flags controlling the text parsing.
* @return A Java <code>Boolean</code> representing the ActionScript
* <code>Boolean</code>, or <code>null</code>.
*/
public abstract Boolean parseBoolean(FlexProject project, String s,
EnumSet<TextParsingFlags> flags);
/**
* Parses an ActionScript <code>int</code> value from a string.
*
* @param project The {@link FlexProject} within which the MXML is being
* parsed.
* @param s The string to be parsed.
* @param flags A set of flags controlling the text parsing.
* @return A Java <code>Integer</code> representing the ActionScript
* <code>int</code>, or <code>null</code>.
*/
public abstract Integer parseInt(FlexProject project, String s,
EnumSet<TextParsingFlags> flags);
/**
* Parses an ActionScript <code>uint</code> value from a string.
*
* @param project The {@link FlexProject} within which the MXML is being
* parsed.
* @param s The string to be parsed.
* @param flags A set of flags controlling the text parsing.
* @return A Java <code>Long</code> representing the ActionScript
* <code>uint</code>, or <code>null</code>.
*/
public abstract Long parseUint(FlexProject project, String s,
EnumSet<TextParsingFlags> flags);
/**
* Parses an ActionScript <code>Number</code> value from a string.
*
* @param project The {@link FlexProject} within which the MXML is being
* parsed.
* @param s The string to be parsed.
* @param flags A set of flags controlling the text parsing.
* @return A Java <code>Number</code> representing the ActionScript
* <code>Number</code>, or <code>null</code>.
*/
public abstract Number parseNumber(FlexProject project, String s,
EnumSet<TextParsingFlags> flags);
/**
* Parses an ActionScript <code>String</code> value from a string.
*
* @param project The {@link FlexProject} within which the MXML is being
* parsed.
* @param s The string to be parsed.
* @param flags A set of flags controlling the text parsing.
* @return A Java <code>String</code> representing the ActionScript
* <code>String</code>, or <code>null</code>.
*/
public abstract String parseString(FlexProject project, String s,
EnumSet<TextParsingFlags> flags);
/**
* Parses an ActionScript <code>Array</code> value from a string.
*
* @param project The {@link FlexProject} within which the MXML is being
* parsed.
* @param s The string to be parsed.
* @param flags A set of flags controlling the text parsing.
* @return A <code>List</code> of Java <code>Object</code> instances
* representing the elements of the ActionScript <code>Array</code>, or
* <code>null</code>.
*/
public abstract List<Object> parseArray(FlexProject project, String s,
EnumSet<TextParsingFlags> flags);
/**
* Parses an ActionScript value from a string.
*
* @param project The {@link FlexProject} within which the MXML is being
* parsed.
* @param s The string to be parsed.
* @param flags A set of flags controlling the text parsing.
* @return A Java <code>Object</code> representing the ActionScript value,
* or <code>null</code>.
*/
public abstract Object parseObject(FlexProject project, String s,
EnumSet<TextParsingFlags> flags);
/**
* Flags that affect how the parsing methods work.
*/
public static enum TextParsingFlags
{
/**
* Recognize array literals, such as <code>"[ 1, 2 ]"</code>.
*/
ALLOW_ARRAY,
/**
* Recognize databinding expressions, such as <code>"{name.first}"</code>.
*/
ALLOW_BINDING,
/**
* Recognize named colors, such as <code>"red"</code>.
*/
ALLOW_COLOR_NAME,
/**
* Recognize compiler directives, such as <code>"@Embed('flag.jpg')"</code>.
*/
ALLOW_COMPILER_DIRECTIVE,
/**
* Allow escaping of compiler directives, so that <code>"\@Embed('flag.jpg')"</code>
* means the text <code>"@Embed('flag.jpg')"</code> and not a directive.
*/
ALLOW_ESCAPED_COMPILER_DIRECTIVE,
/**
* Recognize percent values, such as <code>"100%"</code>.
*/
ALLOW_PERCENT,
/**
* Collapse whitespace into a single space character.
*/
COLLAPSE_WHITE_SPACE,
/**
* Parse as rich text content.
*/
RICH_TEXT_CONTENT
}
}