/* * * 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.ArrayList; import java.util.EnumSet; import java.util.List; import org.apache.flex.compiler.common.XMLName; import org.apache.flex.compiler.constants.IASLanguageConstants; import org.apache.flex.compiler.internal.projects.FlexProject; import org.apache.flex.compiler.mxml.IMXMLLanguageConstants; /** * This singleton class represents the 2006 dialect of MXML, * with the language namespace <code>"http://www.adobe.com/2006/mxml"</code>. * <p> * The special language tags of this dialect are {@code <Binding>}, * {@code <Component>}, {@code <Metadata>}, {@code <Model>}, * {@code <Script>}, and {@code <Style>}. */ public class MXMLDialect2006 extends MXMLDialect { // The singleton instance of this class. private static final MXMLDialect INSTANCE = new MXMLDialect2006(IMXMLLanguageConstants.NAMESPACE_MXML_2006, 2006); /** * Gets the singleton instance of this class. */ public static MXMLDialect getInstance() { return INSTANCE; } // Protected constructor protected MXMLDialect2006(String languageNamespace, int year) { super(languageNamespace, year); bindingXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.BINDING); componentXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.COMPONENT); metadataXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.METADATA); modelXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.MODEL); scriptXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.SCRIPT); styleXMLName = new XMLName(languageNamespace, IMXMLLanguageConstants.STYLE); } @Override public boolean isWhitespace(char c) { // This definition corresponds to the characters // that Java's trim() method trims. return c <= ' '; } @Override public boolean isWhitespace(String s) { int n = s.length(); for (int i = 0; i < n ; i++) { char c = s.charAt(i); if (!isWhitespace(c)) return false; } return true; } @Override public String collapseWhitespace(String s, char replacementChar) { StringBuilder sb = new StringBuilder(); boolean lastWasSpace = true; int n = s.length(); int i = 0; while (i < n) { char c = s.charAt(i++); boolean ws = Character.isWhitespace(c); if (ws) { if (lastWasSpace) ; // consume the character else sb.append(replacementChar); lastWasSpace = true; } else { sb.append(c); lastWasSpace = false; } } return trim(sb.toString()); } @Override public String trim(String s) { return s.trim(); } @Override public String[] splitAndTrim(String s) { // first make sure it isn't in array format int c = s.indexOf('['); if (c != -1) s = s.substring(c + 1); c = s.indexOf(']'); if (c != -1) s = s.substring(0, c); //check for quotes s = s.replace("'", ""); String[] a = s.split(","); if (a == null) return null; int n = a.length; for (int i = 0; i < n; i++) { a[i] = trim(a[i]); } return a; } @Override public Boolean parseBoolean(FlexProject project, String s, EnumSet<TextParsingFlags> flags) { s = trim(s); s = s.toLowerCase(); if (s.equals(IASLanguageConstants.FALSE)) return Boolean.FALSE; else if (s.equals(IASLanguageConstants.TRUE)) return Boolean.TRUE; return null; } @Override public Integer parseInt(FlexProject project, String s, EnumSet<TextParsingFlags> flags) { s = trim(s); // Don't parse ints with leading zeros, which are not octal. // For example, a MA zip code, 02127. if (hasLeadingZeros(s)) return null; Integer value = null; try { value = Integer.decode(s); if (value != null) return value; } catch (NumberFormatException e) { } if (flags != null && flags.contains(TextParsingFlags.ALLOW_COLOR_NAME)) { value = project.getNamedColor(s); if (value != null) return value; } return null; } @Override public Long parseUint(FlexProject project, String s, EnumSet<TextParsingFlags> flags) { s = trim(s); // Don't parse uint's with leading zeros, which are not octal. // For example, a MA zip code, 02127. if (hasLeadingZeros(s)) return null; Long value = null; try { value = Long.decode(s); long longValue = value.longValue(); // TODO I don't understand the purpose of the following logic, // which comes from the old compiler. It seems like it should be // enforcing the positivity of the uint, but doesn't appear to do that. return (longValue == Math.abs(longValue) && longValue <= 0xFFFFFFFFL) ? value : longValue; } catch (NumberFormatException e) { } if (flags != null && flags.contains(TextParsingFlags.ALLOW_COLOR_NAME)) { Integer colorValue = project.getNamedColor(s); if (colorValue != null) return colorValue.longValue(); } return null; } @Override public Number parseNumber(FlexProject project, String s, EnumSet<TextParsingFlags> flags) { // Don't parse Numbers with leading zeros, which are not octal. // For example, a MA zip code, 02127. if (hasLeadingZeros(s)) return null; Integer value = parseInt(project, s, flags); if (value != null) return value; try { return Double.valueOf(s); } catch (NumberFormatException e) { } return null; } @Override public String parseString(FlexProject project, String s, EnumSet<TextParsingFlags> flags) { if (flags != null && flags.contains(TextParsingFlags.COLLAPSE_WHITE_SPACE)) s = collapseWhitespace(s, ' '); return s; } @Override public List<Object> parseArray(FlexProject project, String s, EnumSet<TextParsingFlags> flags) { if (flags != null && flags.contains(TextParsingFlags.ALLOW_ARRAY)) { String trimmed = trim(s); if (!isArray(trimmed)) return null; List<Object> list = new ArrayList<Object>(); if (isEmptyArray(trimmed)) return list; StringBuilder buffer = new StringBuilder(); char quoteChar = '\''; boolean inQuotes = false; int n = trimmed.length(); for (int i = 1; i < n; i++) { char c = trimmed.charAt(i); switch (c) { case '[': { if (inQuotes) { buffer.append(c); } else { // The old compiler did not support nested arrays, // and in fact behaves rather strangely when you // write them. } break; } case '"': case '\'': { if (inQuotes) { if (quoteChar == c) inQuotes = false; else buffer.append(c); } else { inQuotes = true; quoteChar = c; } break; } case ',': case ']': { if (inQuotes) { buffer.append(c); } else { String elementText = trim(buffer.toString()); buffer = new StringBuilder(); // NOTE: Clear any special-processing flags, on the interpretation // that they only apply to top-level scalars. // NOTE: The old compiler did not support nested arrays. Object element = parseObject(project, elementText, null); if (element != null) list.add(element); else return null; } break; } default: { buffer.append(c); break; } } } return list; } return null; } @Override public Object parseObject(FlexProject project, String s, EnumSet<TextParsingFlags> flags) { String trimmed = trim(s); Object result; result = parseBoolean(project, trimmed, flags); if (result != null) return result; result = parseArray(project, trimmed, flags); if (result != null) return result; result = parseNumber(project, trimmed, flags); if (result != null) return result; return s; } // // Other methods // private boolean hasLeadingZeros(String s) { boolean result = false; int n = s.length(); if (n > 1 && s.charAt(0) == '0' && !(s.startsWith("0x") || s.startsWith("0X") || s.startsWith("0."))) { result = true; } return result; } protected boolean isArray(String s) { assert s.equals(trim(s)); int n = s.length(); return n >= 2 && s.charAt(0) == '[' && s.charAt(n - 1) == ']'; } private boolean isEmptyArray(String s) { assert s.equals(trim(s)); boolean result = false; if (isArray(s) && s.substring(1, s.length() - 1).trim().length() == 0) result = true; return result; } }