Package flex2.compiler.css

Source Code of flex2.compiler.css.StyleDef

/*
*
*  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 flex2.compiler.css;

import flash.css.Descriptor;
import flash.css.MediaList;
import flash.css.StyleDeclaration;
import flash.css.StyleDeclarationBlock;
import flash.css.StyleProperty;
import flash.css.StyleSelector;
import flex2.compiler.Source;
import flex2.compiler.mxml.MxmlConfiguration;
import flex2.compiler.mxml.lang.FrameworkDefs;
import flex2.compiler.mxml.reflect.Type;
import flex2.compiler.mxml.rep.AtEmbed;
import flex2.compiler.mxml.rep.MxmlDocument;
import flex2.compiler.util.CompilerMessage.CompilerError;
import flex2.compiler.util.ThreadLocalToolkit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import macromedia.asc.util.ContextStatics;

import org.w3c.flex.forks.css.sac.LexicalUnit;

/**
* A helper class to aggregate style declarations for a particular subject
* (essentially a "type" or in Flex's case, a "component", or the special case
* of 'global' which applies to all components).
*
* From Flex 4 onwards, advanced selector support was added which changes the
* focus of this class and it must now consider multiple style declarations
* for a given subject. In Flex 3 the style declaration would only apply to a
* single type:
*
*     Button { color: #FF0000; }
*
* or a universal class selector:
*
*     .special { color:#FF9900; }
*
* In Flex 4, real class selectors are supported on a type-by-type basis and
* many selectors can target a type:
*
*     Button { color: #FF0000; }
*     Button.special { color: #0000FF; }
*     VBox Panel Button { color: #FF0000; }
*     Button#clickButton { color: #CCCCCC; }
*
* These declarations are used to emit an ActionScript class for the subject
* which is linked into the top level application as a mix-in as needed.
*
* This class is complicated by the fact that it supports both Flex 3 and
* Flex 4 style subsystems independently.
*
* @author Paul Reilly
* @author Pete Farland
*/
public class StyleDef
{
    private String subject;
    private List<String> effectStyles = new ArrayList<String>();

    // Flex 4 properties
    private Map<String, StyleDeclaration> declarations;
    private boolean advanced;

    // Deprecated Flex 3 properties
    private boolean isTypeSelector;
    private Map<String, StyleProperty> styles = new HashMap<String, StyleProperty>();

    private MxmlDocument mxmlDocument;
    private MxmlConfiguration mxmlConfiguration;    // may be null if passed in from a StyleModule
    private Source source;
    private int lineNumber;
    private ContextStatics perCompileData;

    private static final String CLASS_REFERENCE = "ClassReference(";
    private static final String EMBED = "Embed(";
    public static final String GLOBAL = "global"; // A special Flex root selector
    public static final String UNIVERSAL = "*";
    private static final String PROPERTY_REFERENCE = "PropertyReference(";

    // Flex 4 Constructor
    StyleDef(String subject,
            MxmlDocument mxmlDocument,
            MxmlConfiguration mxmlConfiguration,
            Source source,
            int lineNumber,
            ContextStatics perCompileData)
   {
       this.subject = subject;
       this.mxmlDocument = mxmlDocument;
       this.mxmlConfiguration = mxmlConfiguration;
       this.source = source;
       this.lineNumber = lineNumber;
       this.perCompileData = perCompileData;
       advanced = true;
       isTypeSelector = !GLOBAL.equals(subject) && !UNIVERSAL.equals(subject);
   }

    // Deprecated Flex 3 Constructor
    StyleDef(String name,
             boolean isTypeSelector,
             MxmlDocument mxmlDocument,
             MxmlConfiguration mxmlConfiguration,
             Source source,
             int lineNumber,
             ContextStatics perCompileData)
    {
        this.subject = name;
        this.isTypeSelector = isTypeSelector;
        this.mxmlDocument = mxmlDocument;
        this.mxmlConfiguration = mxmlConfiguration;
        this.source = source;
        this.lineNumber = lineNumber;
        this.perCompileData = perCompileData;
    }

    //--------------------------------------------------------------------------
    //
    // Flex 4 Entry Point
    //
    //--------------------------------------------------------------------------

    /**
     * Flex 4 method of declaring styles using advanced selectors, including
     * type selectors, conditional class, id or pseudo-class (states) selectors,
     * and descendant selectors.
     *
     * This method assumes the selector applies to the subject of this StyleDef.
     *
     * @param declaration The style declaration for this rule (which contains
     * the list of property descriptors).
     */
    void addAdvancedDeclaration(StyleDeclaration declaration, MediaList mediaList)
    {
        if (declarations == null)
            declarations = new LinkedHashMap<String, StyleDeclaration>();

        StyleSelector selector = declaration.getSelector();
        String selectorString = selector.toString();
        StyleDeclaration existingDeclaration = declarations.get(selectorString);
        // This may be the first time we've seen this declaration
        if (existingDeclaration == null)
        {
            existingDeclaration = declaration;
            declarations.put(selectorString, existingDeclaration);
        }

        // TODO: We should resolve the differences between Descriptor and
        // StyleProperty and just merge them into the same entity. Special
        // processing could be done on those descriptors directly.

        // Extract the properties from the declaration and record them in a
        // style declaration block. We merge properties from new declaration
        // blocks to existing declaration blocks because selectors can be
        // declared multiple times with different properties. This is primarily
        // done to avoid redundant ClassReferences when it can be determined at
        // compile time that only the last occurrence of a selector with the
        // same property will matter at runtime (because the last one wins).
        // This helps keep SWF size down by avoiding linking in unused classes.
        StyleDeclarationBlock block = existingDeclaration.getDeclarationBlock(selector, mediaList);
        extractProperties(declaration, block.getProperties(), block.getEffectStyles());

        // If the new declaration does not have a media list, we merge its
        // properties into all other existing declaration blocks for this
        // selector that have any of the same properties. This is necessary to
        // ensure that later declaration blocks clobber all earlier declaration
        // blocks.
        if (mediaList == null)
        {
            Map<String, StyleProperty> properties = block.getProperties();

            List<StyleDeclarationBlock> existingBlocks = existingDeclaration.getDeclarationBlocks();
            for (StyleDeclarationBlock existingBlock : existingBlocks)
            {
                if (existingBlock != block)
                {
                    Map<String, StyleProperty> existingProperties = existingBlock.getProperties();
                    for (String property : properties.keySet())
                    {
                        if (existingProperties.get(property) != null)
                            existingProperties.put(property, properties.get(property));
                    }
                }
            }
        }
    }

    //--------------------------------------------------------------------------
    //
    // Deprecated Flex 3 Entry Point
    //
    //--------------------------------------------------------------------------

    /**
     * Deprecated Flex 3 method of declaring styles for a single type or
     * universal class selector. 
     */
    void addDeclaration(StyleDeclaration declaration)
    {
        extractProperties(declaration, styles, effectStyles);
    }

    /**
     * Determines whether this definition expects to generate Flex 4 styled
     * Advanced StyleDeclarations.
     * @return true if advanced style declarations should be generated,
     * otherwise false.
     */
    public boolean isAdvanced()
    {
        return advanced;
    }

    public void setAdvanced(boolean value)
    {
        advanced = value;
    }

    /**
     *
     * @return true if a style manager will store style declarations that are
     * the same as its parent style manager.
     */
    public boolean getAllowDuplicateDefaultStyleDeclarations()
    {
        if (mxmlConfiguration != null)
            return mxmlConfiguration.getAllowDuplicateDefaultStyleDeclarations();
       
        return false;   // default value
    }
   
    public Set<AtEmbed> getAtEmbeds()
    {
        Set<AtEmbed> result = new HashSet<AtEmbed>();

        if (advanced)
        {
            if (declarations != null)
            {
                for (StyleDeclaration styleDeclaration : declarations.values())
                {
                    Collection<StyleDeclarationBlock> declarationBlocks = styleDeclaration.getDeclarationBlocks();
                    for (StyleDeclarationBlock block : declarationBlocks)
                    {
                        Map<String, StyleProperty> properties = block.getProperties();
   
                        if (properties != null)
                        {
                            extractAtEmbeds(result, properties.values());
                        }
                    }
                }
            }
        }
        else if (styles != null)
        {
            extractAtEmbeds(result, styles.values());
        }

        return result;
    }

    /**
     * Extracts the AtEmbeds from the <code>styleProperties</code> and
     * puts them into the <code>atEmbeds</code>.
     */
    private static void extractAtEmbeds(Set<AtEmbed> atEmbeds,
                                        Collection<StyleProperty> styleProperties)
    {
        for (StyleProperty styleProperty : styleProperties)
        {
            Object value = styleProperty.getValue();

            if (value instanceof AtEmbed)
            {
                atEmbeds.add((AtEmbed) value);
            }
        }
    }

    // Flex 4 Method
    /**
     * List of all of the style declarations for the subject represented by
     * this StyleDef.
     */
    public Map<String, StyleDeclaration> getDeclarations()
    {
        return declarations;
    }

    public List<String> getEffectStyles()
    {
        return effectStyles;
    }

    public Set<Import> getImports()
    {
        Set<Import> result = new HashSet<Import>();

        if (advanced)
        {
            if (declarations != null)
            {
                for (StyleDeclaration styleDeclaration : declarations.values())
                {
                    Collection<StyleDeclarationBlock> declarationBlocks = styleDeclaration.getDeclarationBlocks();
                    for (StyleDeclarationBlock block : declarationBlocks)
                    {
                        Map<String, StyleProperty> properties = block.getProperties();

                        if (properties != null)
                        {
                            extractImports(result, properties.values());
                        }
                    }
                }
            }
        }
        else if (styles != null)
        {
            extractImports(result, styles.values());
        }

        return result;
    }

    /**
     * Extracts the imports from the <code>styleProperties</code> and
     * puts them into the <code>imports</code>.
     */
    private static void extractImports(Set<Import> imports,
                                       Collection<StyleProperty> styleProperties)
    {
        for (StyleProperty styleProperty : styleProperties)
        {
            Object value = styleProperty.getValue();

            if (value instanceof Reference)
            {
                Reference reference = (Reference) value;
               
                if (reference.isClassReference() && !reference.getValue().equals("null"))
                {
                    imports.add(new Import(reference.getValue(), reference.getLineNumber()));
                }
            }
        }
    }

    // Deprecated Flex 3 Method
    public int getLineNumber()
    {
        return lineNumber;
    }

    // Deprecated Flex 3 Method
    /**
     * Flex 3 only supported a single "style declaration" per type or universal
     * class selector, so this
     */
    public Map<String, StyleProperty> getStyles()
    {
        return styles;
    }

    /**
     * The subject is defined as the right most simple type selector in a
     * potential chain of selectors. A StyleDef contains all of the style
     * declarations that have selectors with this subject.
     *
     * @return the subject name
     */
    public String getSubject()
    {
        return subject;
    }

    /**
     * The typeName is used to construct the class name of the ActionScript
     * mix-in that represents this subject. Essentially it is the subject
     * name.
     */
    public String getTypeName()
    {
        if (UNIVERSAL.equals(subject))
            return GLOBAL;
        else
            return createTypeName(subject);
    }

    // Deprecated Flex 3 Method
    public boolean isTypeSelector()
    {
        return isTypeSelector;
    }

    //--------------------------------------------------------------------------
    //
    // Helper Methods
    //
    //--------------------------------------------------------------------------

    /**
     * Converts a subject into a safe type name so that it can be used in
     * a generated ActionScript class.
     */
    public static String createTypeName(String subject)
    {
        subject = subject.replace('.', '_');
        return subject;
    }

    /**
     * This method is useful for converting CSS style declaration
     * names, like font-size, into valid ActionScript identifiers,
     * like fontSize.
     */
    public static String dehyphenize(String string)
    {
        StringBuilder stringBuffer = new StringBuilder();

        int start = 0;
        int end = string.indexOf('-');

        while (end >= 0)
        {
            stringBuffer.append( string.substring(start, end) );
            stringBuffer.append( Character.toUpperCase( string.charAt(end + 1) ) );
            start = end + 2;
            end = string.indexOf('-', start);
        }

        stringBuffer.append( string.substring(start) );

        return stringBuffer.toString();
    }

    /**
     * Convert SAC based CSS parser "Descriptors" into simple
     * StyleProperties while looking for special Flex syntax such as
     * Embed(), ClassReference(), and PropertyReference().
     */
    private void extractProperties(StyleDeclaration declaration, Map<String, StyleProperty> properties, List<String> effectsStyles)
    {
        Iterator<Entry<String, Descriptor>> propertyIterator = declaration.iterator();

        while (propertyIterator.hasNext())
        {
            Entry<String, Descriptor> entry = propertyIterator.next();
            Descriptor descriptor = entry.getValue();
            String propertyName = dehyphenize(descriptor.getName());

            try
            {
                if (propertyName.equals("fontFamily"))
                {
                    processFontFamily(properties, descriptor);
                }
                else
                {
                    processPropertyDescriptor(propertyName, descriptor, properties, effectsStyles);
                }
            }
            catch (CompilerError compilerError)
            {
                compilerError.setPath(descriptor.getPath());
                compilerError.setLine(descriptor.getLineNumber());
                ThreadLocalToolkit.log(compilerError);
            }
        }
    }

    /**
     * Process ClassReference and PropertyReference CSS functions.
     */
    private Reference processReference(String value, String styleSheetPath,
                                       int line, boolean isClassReference)
    {
        Reference result = null;
        int prefixLength;

        if (isClassReference)
        {
            prefixLength = CLASS_REFERENCE.length();
        }
        else
        {
            prefixLength = PROPERTY_REFERENCE.length();
        }

        String parameter = value.substring(prefixLength, value.length() - 1).trim();

        if ((parameter.charAt(0) == '"') && (parameter.indexOf('"', 1) == parameter.length() - 1))
        {
            String substring = parameter.substring(1, parameter.length() - 1);

            if (!isClassReference && (mxmlDocument == null))
            {
                PropertyReferenceRequiresDocument propertyReferenceRequiresDocument =
                    new PropertyReferenceRequiresDocument();
                propertyReferenceRequiresDocument.path = styleSheetPath;
                propertyReferenceRequiresDocument.line = line;
                ThreadLocalToolkit.log(propertyReferenceRequiresDocument);
            }
            else
            {
                result = new Reference(substring, isClassReference, styleSheetPath, line);
            }
        }
        else if (parameter.equals("null"))
        {
            result = new Reference(parameter, isClassReference, styleSheetPath, line);
        }
        else
        {
            InvalidReference invalidReference = new InvalidReference(isClassReference);
            invalidReference.path = styleSheetPath;
            invalidReference.line = line;
            ThreadLocalToolkit.log(invalidReference);
        }

        return result;
    }

    private void processFontFamily(Map<String, StyleProperty> properties, Descriptor descriptor)
    {
        String propertyName = "fontFamily";
        String fontFamily = descriptor.getIdentAsString();
        StyleProperty stylesProperty = new StyleProperty(propertyName,
                                                         "\"" + fontFamily + "\"",
                                                         descriptor.getPath(),
                                                         descriptor.getLineNumber());
        properties.put(propertyName, stylesProperty);
    }

    private void processPropertyDescriptor(String propertyName, Descriptor descriptor,
                                           Map<String, StyleProperty> properties,
                                           List<String> effectStyles)
        throws CompilerError
    {
        if (propertyName.endsWith("Effect"))
        {
            effectStyles.add(propertyName);
        }

        Object value = processPropertyValue(descriptor);       

        if (value != null)
        {
            StyleProperty styleProperty = new StyleProperty(propertyName, value,
                                                            descriptor.getPath(),
                                                            descriptor.getLineNumber());
            properties.put(propertyName, styleProperty);
        }
    }

    private Object processPropertyValue(Descriptor descriptor)
        throws CompilerError
    {
        String value = descriptor.getValueAsString();
        Object result = value;

        if (value.startsWith(EMBED))
        {
            result = AtEmbed.create(perCompileData, source, value,
                                    descriptor.getPath(), descriptor.getLineNumber(),
                                    "_embed_css_");
        }
        else if (value.startsWith(CLASS_REFERENCE))
        {
            result = processReference(value, descriptor.getPath(), descriptor.getLineNumber(), true);
        }
        else if (value.startsWith(PROPERTY_REFERENCE))
        {
            result = processReference(value, descriptor.getPath(), descriptor.getLineNumber(), false);
        }

        // Strip the quotes for CSS identifiers, which are properties
        // of the Mxml document.  This allows us to support minimal
        // data binding functionality for the CSS object styling feature.
        //
        // The "advanced" check is really a check for compatibility
        // version greater than 3.0.  If the way advanced gets set
        // changes, this should be updated.
        //
        if (!advanced &&
            (mxmlDocument != null) &&
            (descriptor.getValue().getLexicalUnitType() == LexicalUnit.SAC_IDENT) &&
            (value != null) &&
            (value.length() > 2) &&
            ((value.charAt(0) == '\'') || (value.charAt(0) == '\"')) &&
            ((value.charAt(value.length() - 1) == '\'') || (value.charAt(value.length() - 1) == '\"')))
        {
            String potentialProperty = value.substring(1, value.length() - 1);

            Type type = mxmlDocument.getRoot().getType();

            if (type != null && type.getProperty(potentialProperty) != null)
            {
                result = potentialProperty;
            }

            // deprecated - Flex 1.5 support only
            if ( FrameworkDefs.isBuiltinEffectName(potentialProperty) )
            {
                mxmlDocument.addTypeRef(mxmlDocument.getStandardDefs().getEffectsPackage() + '.' + potentialProperty,
                                        descriptor.getLineNumber());
            }
        }

        return result;
    }

    //--------------------------------------------------------------------------
    //
    // Errors
    //
    //--------------------------------------------------------------------------

    public static class InvalidReference extends CompilerError
    {
        private static final long serialVersionUID = 3730898410175891394L;
        public String type;

        public InvalidReference(boolean isClassReference)
        {
            if (isClassReference)
            {
                type = "Class";
            }
            else
            {
                type = "Property";
            }
        }
    }

    public static class PropertyReferenceRequiresDocument extends CompilerError
    {
        private static final long serialVersionUID = 3730898410175891396L;

        public PropertyReferenceRequiresDocument()
        {
        }
    }
}
TOP

Related Classes of flex2.compiler.css.StyleDef

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.