| /* |
| * |
| * 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.driver.js.flexjs; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.apache.flex.compiler.constants.IASLanguageConstants; |
| import org.apache.flex.compiler.css.ICSSDocument; |
| import org.apache.flex.compiler.css.ICSSMediaQueryCondition; |
| import org.apache.flex.compiler.css.ICSSProperty; |
| import org.apache.flex.compiler.css.ICSSPropertyValue; |
| import org.apache.flex.compiler.css.ICSSRule; |
| import org.apache.flex.compiler.css.ICSSSelector; |
| import org.apache.flex.compiler.css.ICSSSelectorCondition; |
| import org.apache.flex.compiler.internal.codegen.js.goog.JSGoogEmitterTokens; |
| import org.apache.flex.compiler.internal.css.*; |
| import org.apache.flex.compiler.internal.css.codegen.CSSCompilationSession; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.ImmutableList; |
| |
| public class JSCSSCompilationSession extends CSSCompilationSession |
| { |
| |
| private ArrayList<String> requires; |
| |
| public String getEncodedCSS() |
| { |
| final ICSSDocument css = synthesisNormalizedCSS(); |
| StringBuilder sb = new StringBuilder(); |
| requires = new ArrayList<String>(); |
| encodeCSS(css, sb); |
| if (sb.length() == 0) |
| return null; |
| sb.append("];\n"); |
| for (String r : requires) |
| { |
| sb.append(JSGoogEmitterTokens.GOOG_REQUIRE.getToken() + "('" + formatQualifiedName(r) + "');\n"); |
| } |
| |
| return sb.toString(); |
| } |
| |
| public String emitCSS() |
| { |
| final ICSSDocument css = synthesisNormalizedCSS(); |
| StringBuilder sb = new StringBuilder(); |
| sb.append("/* Generated by Apache Flex Cross-Compiler */\n"); |
| walkCSS(css, sb); |
| return sb.toString(); |
| } |
| |
| private String fontFaceToString(CSSFontFace fontFace) |
| { |
| final StringBuilder result = new StringBuilder(); |
| result.append("@font-face {\n"); |
| result.append(" "); |
| result.append("font-family: "); |
| result.append(fontFace.getFontFamily() + ";\n"); |
| result.append(" "); |
| result.append("font-style: "); |
| result.append(fontFace.getFontStyle() + ";\n"); |
| result.append(" "); |
| result.append("font-weight: "); |
| result.append(fontFace.getFontStyle() + ";\n"); |
| result.append(" "); |
| ArrayList<ICSSPropertyValue> sources = fontFace.getSources(); |
| for (ICSSPropertyValue src : sources) |
| { |
| result.append("src: "); |
| result.append(src.toString() + ";\n"); |
| } |
| result.append("}\n"); |
| return result.toString(); |
| } |
| |
| private String cssRuleToString(ICSSRule rule) |
| { |
| final StringBuilder result = new StringBuilder(); |
| |
| ImmutableList<ICSSMediaQueryCondition> mqList = rule.getMediaQueryConditions(); |
| boolean hasMediaQuery = !mqList.isEmpty(); |
| if (hasMediaQuery) |
| { |
| result.append("@media "); |
| result.append(Joiner.on(" and ").join(rule.getMediaQueryConditions())); |
| result.append(" {\n"); |
| result.append(" "); |
| } |
| |
| ImmutableList<ICSSSelector> selectors = rule.getSelectorGroup(); |
| boolean firstOne = true; |
| for (ICSSSelector selector : selectors) |
| { |
| String s = selector.toString(); |
| // add "." to type selectors that don't map cleanly |
| // to CSS type selectors to convert them to class |
| // selectors. |
| if (!s.startsWith(".") && !s.startsWith("*") && !s.startsWith("#")) |
| { |
| String condition = null; |
| int colon = s.indexOf(":"); |
| if (colon != -1) |
| { |
| condition = s.substring(colon); |
| s = s.substring(0, colon); |
| } |
| if (!htmlElementNames.contains(s.toLowerCase())) |
| { |
| int pipe = s.indexOf("|"); |
| if (pipe != -1) |
| s = s.substring(pipe + 1); |
| s = "." + s; |
| } |
| if (condition != null) |
| s = s + condition; |
| } |
| if (!firstOne) |
| result.append(",\n"); |
| result.append(s); |
| } |
| |
| result.append(" {\n"); |
| for (final ICSSProperty prop : rule.getProperties()) |
| { |
| if (!hasMediaQuery) |
| result.append(" "); |
| |
| String propString = ((CSSProperty)prop).toCSSString(); |
| // skip class references since the won't work in CSS |
| if (propString.contains("ClassReference")) |
| continue; |
| result.append(" ").append(propString).append("\n"); |
| } |
| if (hasMediaQuery) |
| result.append(" }\n"); |
| |
| result.append("}\n"); |
| |
| return result.toString(); |
| } |
| |
| private void walkCSS(ICSSDocument css, StringBuilder sb) |
| { |
| for (CSSFontFace fontFace : fontFaces) |
| { |
| sb.append(fontFaceToString(fontFace)); |
| } |
| if (fontFaces.size() > 0) |
| sb.append("\n\n"); |
| ImmutableList<ICSSRule> rules = css.getRules(); |
| for (ICSSRule rule : rules) |
| { |
| String s = cssRuleToString(rule); |
| if (s.startsWith("@media -flex-flash")) |
| continue; |
| if (s.startsWith(".global {")) |
| s = s.replace(".global {", "* {"); |
| sb.append(s); |
| sb.append("\n\n"); |
| } |
| } |
| |
| private void encodeCSS(ICSSDocument css, StringBuilder sb) |
| { |
| ImmutableList<ICSSRule> rules = css.getRules(); |
| boolean skipcomma = true; |
| for (ICSSRule rule : rules) |
| { |
| String s = encodeRule(rule); |
| if (s != null) |
| { |
| if (skipcomma) |
| skipcomma = false; |
| else |
| sb.append(",\n"); |
| sb.append(s); |
| } |
| } |
| } |
| |
| List<String> htmlElementNames = Arrays.asList( |
| "body", |
| "button", |
| "span" |
| ); |
| |
| private String encodeRule(ICSSRule rule) |
| { |
| final StringBuilder result = new StringBuilder(); |
| |
| ImmutableList<ICSSMediaQueryCondition> mqlist = rule.getMediaQueryConditions(); |
| int n = mqlist.size(); |
| if (n > 0) |
| { |
| if (mqlist.get(0).toString().equals("-flex-flash")) |
| return null; |
| |
| result.append(n); |
| |
| for (ICSSMediaQueryCondition mqcond : mqlist) |
| { |
| result.append(",\n"); |
| result.append("\"" + mqcond.toString() + "\""); |
| } |
| } |
| else |
| result.append(n); |
| |
| result.append(",\n"); |
| |
| ImmutableList<ICSSSelector> slist = rule.getSelectorGroup(); |
| result.append(slist.size()); |
| |
| for (ICSSSelector sel : slist) |
| { |
| result.append(",\n"); |
| String selName = this.resolvedSelectors.get(sel); |
| if (selName == null || selName.equals("null")) |
| result.append("\"" + sel.toString() + "\""); |
| else |
| { |
| selName = formatQualifiedName(selName); |
| ImmutableList<ICSSSelectorCondition> conds = sel.getConditions(); |
| for (ICSSSelectorCondition cond : conds) |
| selName += cond.toString(); |
| result.append("\"" + selName + "\""); |
| } |
| } |
| result.append(",\n"); |
| result.append("function() {"); |
| |
| ImmutableList<ICSSProperty> plist = rule.getProperties(); |
| |
| boolean firstProp = true; |
| for (final ICSSProperty prop : plist) |
| { |
| if (!firstProp) |
| result.append(";\n"); |
| firstProp = false; |
| result.append("this[\"" + prop.getName() + "\"] = "); |
| ICSSPropertyValue value = prop.getValue(); |
| if (value instanceof CSSArrayPropertyValue) |
| { |
| ImmutableList<? extends ICSSPropertyValue> values = ((CSSArrayPropertyValue)value).getElements(); |
| result.append("["); |
| boolean firstone = true; |
| for (ICSSPropertyValue val : values) |
| { |
| if (firstone) |
| firstone = false; |
| else |
| result.append(", "); |
| if (val instanceof CSSStringPropertyValue) |
| { |
| result.append("\"" + ((CSSStringPropertyValue)val).getValue() + "\""); |
| } |
| else if (val instanceof CSSColorPropertyValue) |
| { |
| result.append(new Integer(((CSSColorPropertyValue)val).getColorAsInt())); |
| } |
| else if (val instanceof CSSRgbColorPropertyValue) |
| { |
| result.append(new Integer(((CSSRgbColorPropertyValue)val).getColorAsInt())); |
| } |
| else if (value instanceof CSSRgbaColorPropertyValue) |
| { |
| //todo: handle alpha in the RGBA ? |
| result.append(new Integer(((CSSRgbaColorPropertyValue)value).getColorAsInt())); |
| } |
| else if (val instanceof CSSKeywordPropertyValue) |
| { |
| CSSKeywordPropertyValue keywordValue = (CSSKeywordPropertyValue)val; |
| String keywordString = keywordValue.getKeyword(); |
| if (IASLanguageConstants.TRUE.equals(keywordString)) |
| result.append("true"); |
| else if (IASLanguageConstants.FALSE.equals(keywordString)) |
| result.append("false"); |
| else |
| result.append("\"" + ((CSSKeywordPropertyValue)val).getKeyword() + "\""); |
| } |
| else if (val instanceof CSSNumberPropertyValue) |
| { |
| result.append(new Double(((CSSNumberPropertyValue)val).getNumber().doubleValue())); |
| } |
| else if (val instanceof CSSURLAndFormatPropertyValue) |
| { |
| result.append("\"" + ((CSSURLAndFormatPropertyValue)val).toString() + "\""); |
| } |
| else |
| { |
| result.append("unexpected value type: " + val.toString()); |
| } |
| } |
| result.append("]"); |
| } |
| else if (value instanceof CSSStringPropertyValue) |
| { |
| result.append("\"" + ((CSSStringPropertyValue)value).getValue() + "\""); |
| } |
| else if (value instanceof CSSColorPropertyValue) |
| { |
| result.append(new Integer(((CSSColorPropertyValue)value).getColorAsInt())); |
| } |
| else if (value instanceof CSSRgbColorPropertyValue) |
| { |
| result.append(new Integer(((CSSRgbColorPropertyValue)value).getColorAsInt())); |
| } |
| else if (value instanceof CSSRgbaColorPropertyValue) |
| { |
| //todo: handle alpha in the RGBA ? |
| result.append(new Integer(((CSSRgbaColorPropertyValue)value).getColorAsInt())); |
| } |
| else if (value instanceof CSSKeywordPropertyValue) |
| { |
| CSSKeywordPropertyValue keywordValue = (CSSKeywordPropertyValue)value; |
| String keywordString = keywordValue.getKeyword(); |
| if (IASLanguageConstants.TRUE.equals(keywordString)) |
| result.append("true"); |
| else if (IASLanguageConstants.FALSE.equals(keywordString)) |
| result.append("false"); |
| else |
| result.append("\"" + ((CSSKeywordPropertyValue)value).getKeyword() + "\""); |
| } |
| else if (value instanceof CSSNumberPropertyValue) |
| { |
| result.append(new Double(((CSSNumberPropertyValue)value).getNumber().doubleValue())); |
| } |
| else if (value instanceof CSSFunctionCallPropertyValue) |
| { |
| final CSSFunctionCallPropertyValue functionCall = (CSSFunctionCallPropertyValue)value; |
| if ("ClassReference".equals(functionCall.name)) |
| { |
| final String className = CSSFunctionCallPropertyValue.getSingleArgumentFromRaw(functionCall.rawArguments); |
| if ("null".equals(className)) |
| { |
| // ClassReference(null) resets the property's class reference. |
| result.append("null"); |
| } |
| else |
| { |
| result.append(formatQualifiedName(className)); |
| requires.add(className); |
| } |
| } |
| else if ("url".equals(functionCall.name)) |
| { |
| final String urlString = CSSFunctionCallPropertyValue.getSingleArgumentFromRaw(functionCall.rawArguments); |
| result.append("\"" + urlString + "\""); |
| } |
| else if ("PropertyReference".equals(functionCall.name)) |
| { |
| // TODO: implement me |
| } |
| else if ("calc".equals(functionCall.name)) |
| { |
| // TODO: implement me |
| } |
| else if ("Embed".equals(functionCall.name)) |
| { |
| // TODO: implement me |
| /* |
| final ICompilerProblem e = new CSSCodeGenProblem( |
| new IllegalStateException("Unable to find compilation unit for " + functionCall)); |
| problems.add(e); |
| */ |
| } |
| else |
| { |
| assert false : "CSS parser bug: unexpected function call property value: " + functionCall; |
| throw new IllegalStateException("Unexpected function call property value: " + functionCall); |
| } |
| } |
| } |
| result.append("}"); |
| |
| return result.toString(); |
| |
| } |
| |
| @Override |
| protected boolean keepRule(ICSSRule newRule) |
| { |
| if (super.keepRule(newRule)) |
| return true; |
| |
| // include all rules not found in defaults.css |
| // theoretically, defaults.css rules were |
| // properly added in the super call. |
| String sp = newRule.getSourcePath(); |
| if (!sp.contains("defaults.css")) |
| return true; |
| |
| return false; |
| } |
| |
| private String formatQualifiedName(String name) |
| { |
| /* |
| if (name.contains("goog.") || name.startsWith("Vector.")) |
| return name; |
| if (name.startsWith(".")) |
| { |
| return "." + name.substring(1).replaceAll("\\.", "_"); |
| } |
| name = name.replaceAll("\\.", "_"); |
| */ |
| return name; |
| } |
| |
| } |