Package org.pentaho.reporting.engine.classic.core.style.css

Source Code of org.pentaho.reporting.engine.classic.core.style.css.SimpleStyleRuleMatcher

/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2013 Pentaho Corporation..  All rights reserved.
*/

package org.pentaho.reporting.engine.classic.core.style.css;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;

import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.Element;
import org.pentaho.reporting.engine.classic.core.ReportElement;
import org.pentaho.reporting.engine.classic.core.Section;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleSheet;
import org.pentaho.reporting.engine.classic.core.style.css.namespaces.NamespaceCollection;
import org.pentaho.reporting.engine.classic.core.style.css.namespaces.NamespaceDefinition;
import org.pentaho.reporting.engine.classic.core.style.css.selector.CSSSelector;
import org.pentaho.reporting.engine.classic.core.style.css.selector.SelectorWeight;
import org.pentaho.reporting.engine.classic.core.util.beans.BeanException;
import org.pentaho.reporting.engine.classic.core.util.beans.ConverterRegistry;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceKeyCreationException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.w3c.css.sac.AttributeCondition;
import org.w3c.css.sac.CombinatorCondition;
import org.w3c.css.sac.Condition;
import org.w3c.css.sac.ConditionalSelector;
import org.w3c.css.sac.DescendantSelector;
import org.w3c.css.sac.ElementSelector;
import org.w3c.css.sac.NegativeCondition;
import org.w3c.css.sac.NegativeSelector;
import org.w3c.css.sac.Selector;
import org.w3c.css.sac.SiblingSelector;
import org.w3c.css.sac.SimpleSelector;

/**
* A stateless implementation of the style rule matching. This implementation is
* stateless within the current layout process.
*
* @author Thomas Morgner
*/
public class SimpleStyleRuleMatcher implements StyleRuleMatcher
{
  private ResourceManager resourceManager;
  private ElementStyleRule[] activeStyleRules;
  private ElementStyleRule[] activePseudoStyleRules;
  private NamespaceCollection namespaces;
  private DocumentContext context;

  public SimpleStyleRuleMatcher()
  {
  }

  public void initialize(final DocumentContext layoutProcess)
  {
    if (layoutProcess == null)
    {
      throw new NullPointerException();
    }
    this.context = layoutProcess;
    this.resourceManager = layoutProcess.getResourceManager();

    final ArrayList<CSSCounterRule> counterRules = new ArrayList<CSSCounterRule>();
    final ArrayList<ElementStyleRule> styleRules = new ArrayList<ElementStyleRule>();
    final DocumentContext dc = this.context;

    namespaces = dc.getNamespaces();

    if (dc.getStyleResource() != null)
    {
      handleLinkNode(dc.getStyleResource(), styleRules, counterRules);
    }
    if (dc.getStyleDefinition() != null)
    {
      handleStyleNode(dc.getStyleDefinition(), styleRules, counterRules);
    }

    activeStyleRules = styleRules.toArray(new ElementStyleRule[styleRules.size()]);

    styleRules.clear();
    for (int i = 0; i < activeStyleRules.length; i++)
    {
      final ElementStyleRule activeStyleRule = activeStyleRules[i];
      if (isPseudoElementRule(activeStyleRule) == false)
      {
        continue;
      }
      styleRules.add(activeStyleRule);
    }
    activePseudoStyleRules = styleRules.toArray(new ElementStyleRule[styleRules.size()]);

  }

  private void handleLinkNode(final Object styleResource,
                              final ArrayList<ElementStyleRule> styleRules,
                              final ArrayList<CSSCounterRule> counterRules)
  {
    // do some external parsing
    // (Same as the <link> element of HTML)
    try
    {
      final String href = (String) styleResource;
      final ResourceKey baseKey = context.getContextKey();

      final ResourceKey derivedKey;
      if (baseKey == null)
      {
        derivedKey = resourceManager.createKey(href);
      }
      else
      {
        derivedKey = resourceManager.deriveKey(baseKey, String.valueOf(href));
      }

      final ElementStyleDefinition styleSheet = parseStyleSheet(derivedKey, null);
      if (styleSheet == null)
      {
        return;
      }
      addStyleRules(styleSheet, styleRules);
      addCounterRules(styleSheet, counterRules);
    }
    catch (ResourceKeyCreationException e)
    {
      e.printStackTrace();
    }
  }


  private void handleStyleNode(final ElementStyleDefinition node,
                               final ArrayList<ElementStyleRule> styleRules,
                               final ArrayList<CSSCounterRule> counterRules)
  {
    addStyleRules(node, styleRules);
    addCounterRules(node, counterRules);
  }


  private void addCounterRules(final ElementStyleDefinition styleSheet,
                               final ArrayList<CSSCounterRule> rules)
  {
    final int sc = styleSheet.getStyleSheetCount();
    for (int i = 0; i < sc; i++)
    {
      addCounterRules(styleSheet.getStyleSheet(i), rules);
    }

    final int rc = styleSheet.getRuleCount();
    for (int i = 0; i < rc; i++)
    {
      final ElementStyleSheet rule = styleSheet.getRule(i);
      if (rule instanceof CSSCounterRule)
      {
        final CSSCounterRule drule = (CSSCounterRule) rule;
        rules.add(drule);
      }
    }
  }

  private void addStyleRules(final ElementStyleDefinition styleSheet,
                             final ArrayList<ElementStyleRule> activeStyleRules)
  {
    final int sc = styleSheet.getStyleSheetCount();
    for (int i = 0; i < sc; i++)
    {
      addStyleRules(styleSheet.getStyleSheet(i), activeStyleRules);
    }

    final int rc = styleSheet.getRuleCount();
    for (int i = 0; i < rc; i++)
    {
      final ElementStyleSheet rule = styleSheet.getRule(i);
      if (rule instanceof ElementStyleRule)
      {
        final ElementStyleRule drule = (ElementStyleRule) rule;
        activeStyleRules.add(drule);
      }
    }
  }

  private ElementStyleDefinition parseStyleSheet(final ResourceKey key,
                                                 final ResourceKey context)
  {
    try
    {
      final Resource resource = resourceManager.create
          (key, context, ElementStyleDefinition.class);
      return (ElementStyleDefinition) resource.getResource();
    }
    catch (ResourceException e)
    {
      // Log.info("Unable to parse StyleSheet: " + e.getLocalizedMessage());
    }
    return null;
  }

  private boolean isPseudoElementRule(final ElementStyleRule rule)
  {
    final List<CSSSelector> selectorList = rule.getSelectorList();
    for (int i = 0; i < selectorList.size(); i += 1)
    {
      final CSSSelector selector = selectorList.get(i);
      if (selector == null)
      {
        continue;
      }

      if (selector.getSelectorType() != Selector.SAC_CONDITIONAL_SELECTOR)
      {
        continue;
      }

      final ConditionalSelector cs = (ConditionalSelector) selector;
      final Condition condition = cs.getCondition();
      if (condition.getConditionType() != Condition.SAC_PSEUDO_CLASS_CONDITION)
      {
        continue;
      }
      return true;
    }
    return false;
  }

  public boolean isMatchingPseudoElement(final ReportElement element, final String pseudo)
  {
    for (int i = 0; i < activePseudoStyleRules.length; i++)
    {
      final ElementStyleRule activeStyleRule = activePseudoStyleRules[i];
      final List<CSSSelector> selectorList = activeStyleRule.getSelectorList();
      for (int x = 0; x < selectorList.size(); x += 1)
      {
        final CSSSelector selector = selectorList.get(x);
        if (selector instanceof ConditionalSelector == false)
        {
          continue;
        }

        final ConditionalSelector cs = (ConditionalSelector) selector;
        final Condition condition = cs.getCondition();

        final AttributeCondition ac = (AttributeCondition) condition;
        if (ObjectUtilities.equal(ac.getValue(), pseudo) == false)
        {
          continue;
        }

        final SimpleSelector simpleSelector = cs.getSimpleSelector();
        if (isMatch(element, simpleSelector))
        {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * Creates an independent copy of this style rule matcher.
   *
   * @return this instance, as this implementation is stateless
   */
  public StyleRuleMatcher deriveInstance()
  {
    return this;
  }

  /**
   * Returns all matching rules for the given element. Each matched rule must carry the weight of the matching
   * selector.
   *
   * @param element
   * @return
   */
  public MatcherResult[] getMatchingRules(final ReportElement element)
  {
    final ArrayList<MatcherResult> retvals = new ArrayList<MatcherResult>();
    for (int i = 0; i < activeStyleRules.length; i++)
    {
      final ElementStyleRule activeStyleRule = activeStyleRules[i];
      final List<CSSSelector> selectorList = activeStyleRule.getSelectorList();
      SelectorWeight weight = null;

      for (int x = 0; x < selectorList.size(); x += 1)
      {
        final CSSSelector selector = selectorList.get(x);
        if (selector == null)
        {
          continue;
        }

        if (isMatch(element, selector))
        {
          if (weight == null)
          {
            weight = selector.getWeight();
          }
          else
          {
            if (weight.compareTo(selector.getWeight()) < 0)
            {
              weight = selector.getWeight();
            }
          }
        }
      }
      if (weight != null)
      {
        retvals.add(new MatcherResult(weight, activeStyleRule));
      }
    }

//    Log.debug ("Got " + retvals.size() + " matching rules for " +
//            layoutContext.getTagName() + ":" +
//            layoutContext.getPseudoElement());

    return retvals.toArray(new MatcherResult[retvals.size()]);
  }

  private boolean isMatch(final ReportElement node,
                          final Selector selector)
  {
    final short selectorType = selector.getSelectorType();
    switch (selectorType)
    {
      case Selector.SAC_ANY_NODE_SELECTOR:
        return true;
      case Selector.SAC_ROOT_NODE_SELECTOR:
        return node.getParentSection() == null;
      case Selector.SAC_NEGATIVE_SELECTOR:
      {
        final NegativeSelector negativeSelector = (NegativeSelector) selector;
        return isMatch(node, negativeSelector) == false;
      }
      case Selector.SAC_DIRECT_ADJACENT_SELECTOR:
      {
        final SiblingSelector silbSelect = (SiblingSelector) selector;
        return isSilblingMatch(node, silbSelect);
      }
      case Selector.SAC_PSEUDO_ELEMENT_SELECTOR:
      {
        return false;
      }
      case Selector.SAC_ELEMENT_NODE_SELECTOR:
      {
        final ElementSelector es = (ElementSelector) selector;
        final String localName = es.getLocalName();
        if (localName != null)
        {
          if (localName.equals(getTagName(node)) == false)
          {
            return false;
          }
        }
        final String namespaceURI = es.getNamespaceURI();
        if (namespaceURI != null)
        {
          final String namespace = getNamespace(node);
          if (namespaceURI.equals(namespace) == false)
          {
            return false;
          }
        }
        return true;
      }
      case Selector.SAC_CHILD_SELECTOR:
      {
        final DescendantSelector ds = (DescendantSelector) selector;
        if (isMatch(node, ds.getSimpleSelector()) == false)
        {
          return false;
        }
        final ReportElement parent = node.getParentSection();
        return (isMatch(parent, ds.getAncestorSelector()));
      }
      case Selector.SAC_DESCENDANT_SELECTOR:
      {
        final DescendantSelector ds = (DescendantSelector) selector;
        if (isMatch(node, ds.getSimpleSelector()) == false)
        {
          return false;
        }
        return (isDescendantMatch(node, ds.getAncestorSelector()));
      }
      case Selector.SAC_CONDITIONAL_SELECTOR:
      {
        final ConditionalSelector cs = (ConditionalSelector) selector;
        if (evaluateCondition(node, cs.getCondition()) == false)
        {
          return false;
        }
        if (isMatch(node, cs.getSimpleSelector()) == false)
        {
          return false;
        }
        return true;
      }
      default:
        return false;
    }
  }

  private boolean evaluateCondition(final ReportElement node,
                                    final Condition condition)
  {
    switch (condition.getConditionType())
    {
      case Condition.SAC_AND_CONDITION:
      {
        final CombinatorCondition cc = (CombinatorCondition) condition;
        return (evaluateCondition(node, cc.getFirstCondition()) &&
            evaluateCondition(node, cc.getSecondCondition()));
      }
      case Condition.SAC_OR_CONDITION:
      {
        final CombinatorCondition cc = (CombinatorCondition) condition;
        return (evaluateCondition(node, cc.getFirstCondition()) ||
            evaluateCondition(node, cc.getSecondCondition()));
      }
      case Condition.SAC_ATTRIBUTE_CONDITION:
      {
        final AttributeCondition ac = (AttributeCondition) condition;
        final Object attr = queryAttribute(node, ac);

        if (ac.getValue() == null)
        {
          // dont care what's inside, as long as there is a value ..
          return attr != null;
        }
        else
        {
          return ObjectUtilities.equal(attr, ac.getValue());
        }
      }
      case Condition.SAC_CLASS_CONDITION:
      {
        final AttributeCondition ac = (AttributeCondition) condition;
        String namespace = getNamespace(node);
        if (namespace == null)
        {
          namespace = namespaces.getDefaultNamespaceURI();
        }
        if (namespace == null)
        {
          return false;
        }
        final NamespaceDefinition ndef = namespaces.getDefinition(namespace);
        if (ndef == null)
        {
          return false;
        }
        final String[] classAttribute = ndef.getClassAttribute(getTagName(node));
        for (int i = 0; i < classAttribute.length; i++)
        {
          final String attr = classAttribute[i];
          final String htmlAttr = (String) node.getAttribute(namespace, attr);
          if (isOneOfAttributes(htmlAttr, ac.getValue()))
          {
            return true;
          }
        }
        return false;
      }
      case Condition.SAC_ID_CONDITION:
      {
        final AttributeCondition ac = (AttributeCondition) condition;
        final Object id = node.getAttribute(AttributeNames.Xml.NAMESPACE, AttributeNames.Xml.ID);
        return ObjectUtilities.equal(ac.getValue(), id);
      }
      case Condition.SAC_LANG_CONDITION:
      {
        final AttributeCondition ac = (AttributeCondition) condition;
        final Locale locale = getLanguage(node);
        final String lang = locale.getLanguage();
        return isBeginHyphenAttribute(lang, ac.getValue());
      }
      case Condition.SAC_NEGATIVE_CONDITION:
      {
        final NegativeCondition nc = (NegativeCondition) condition;
        return evaluateCondition(node, nc.getCondition()) == false;
      }
      case Condition.SAC_ONE_OF_ATTRIBUTE_CONDITION:
      {
        final AttributeCondition ac = (AttributeCondition) condition;
        final Object o = queryAttribute(node, ac);
        if (o == null)
        {
          return false;
        }
       
        try
        {
          final String attr = ConverterRegistry.toAttributeValue(o);
          return isOneOfAttributes(attr, ac.getValue());
        }
        catch (BeanException e)
        {
          return false;
        }
      }
      case Condition.SAC_PSEUDO_CLASS_CONDITION:
      {
        final AttributeCondition ac = (AttributeCondition) condition;
        final String pseudoClass = getPseudoElement(node);
        if (pseudoClass == null)
        {
          return false;
        }
        if (pseudoClass.equals(ac.getValue()))
        {
          return true;
        }
        return false;
      }
      case Condition.SAC_ONLY_CHILD_CONDITION:
      case Condition.SAC_ONLY_TYPE_CONDITION:
      case Condition.SAC_POSITIONAL_CONDITION:
      case Condition.SAC_CONTENT_CONDITION:
      default:
      {
        // any of these conditionals are not yet implemented. They are defined as part of the CSS standard.
        return false;
      }
    }
  }

  private Object queryAttribute(final ReportElement node, final AttributeCondition ac)
  {
    final String namespaceURI = ac.getNamespaceURI();
    final Object attr;
    if (namespaceURI == null)
    {
      attr = node.getFirstAttribute(ac.getLocalName());
    }
    else
    {
      attr = node.getAttribute(namespaceURI, ac.getLocalName());
    }
    return attr;
  }

  private String getPseudoElement(final ReportElement node)
  {
    // at the moment we do not support pseudo-elements.
    return null;
  }

  private String getNamespace(final ReportElement node)
  {
    return AttributeNames.Core.NAMESPACE;
  }

  private String getTagName(final ReportElement node)
  {
    return node.getElementType().getMetaData().getName();
  }

  private Locale getLanguage(final ReportElement node)
  {
    return null;
  }

  private boolean isOneOfAttributes(final String attrValue, final String value)
  {
    if (attrValue == null)
    {
      return false;
    }
    if (attrValue.equals(value))
    {
      return true;
    }

    final StringTokenizer strTok = new StringTokenizer(attrValue);
    while (strTok.hasMoreTokens())
    {
      final String token = strTok.nextToken();
      if (token.equals(value))
      {
        return true;
      }
    }
    return false;
  }

  private boolean isBeginHyphenAttribute(final String attrValue, final String value)
  {
    if (attrValue == null)
    {
      return false;
    }
    if (value == null)
    {
      return false;
    }
    return (attrValue.startsWith(value));

  }

  private boolean isDescendantMatch(final ReportElement node,
                                    final Selector selector)
  {
    ReportElement parent = node.getParentSection();
    while (parent != null)
    {
      if (isMatch(parent, selector))
      {
        return true;
      }
      parent = parent.getParentSection();
    }
    return false;
  }

  private boolean isSilblingMatch(final ReportElement node,
                                  final SiblingSelector select)
  {
    ReportElement pred = getPreviousReportElement(node);
    while (pred != null)
    {
      if (isMatch(pred, select))
      {
        return true;
      }
      pred = getPreviousReportElement(pred);
    }
    return false;
  }

  private ReportElement getPreviousReportElement(final ReportElement e)
  {
    final Section parentSection = e.getParentSection();
    if (parentSection == null)
    {
      return null;
    }

    final int count = parentSection.getElementCount();
    for (int i = 0; i < count; i += 1)
    {
      final Element element = parentSection.getElement(i);
      if (e == element)
      {
        if (i == 0)
        {
          return null;
        }
        else
        {
          return parentSection.getElement(i - 1);
        }
      }
    }
    return null;
  }
}
TOP

Related Classes of org.pentaho.reporting.engine.classic.core.style.css.SimpleStyleRuleMatcher

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.