Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.daisy.pipeline.braille.css.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.SortedMap;
import java.util.TreeMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import cz.vutbr.web.css.CSSProperty;
import cz.vutbr.web.css.Declaration;
import cz.vutbr.web.css.Rule;
import cz.vutbr.web.css.RuleBlock;
import cz.vutbr.web.css.RuleMargin;
import cz.vutbr.web.css.RulePage;
import cz.vutbr.web.css.Selector;
import cz.vutbr.web.css.Selector.Combinator;
import cz.vutbr.web.css.Selector.PseudoClass;
import cz.vutbr.web.css.Selector.SelectorPart;
import cz.vutbr.web.css.SourceLocator;
import cz.vutbr.web.css.Term;
import org.daisy.braille.css.BrailleCSSParserFactory.Context;
import org.daisy.braille.css.BrailleCSSProperty.Content;
import org.daisy.braille.css.BrailleCSSProperty.StringSet;
import org.daisy.braille.css.BrailleCSSProperty.TextTransform;
import org.daisy.braille.css.InlineStyle;
import org.daisy.braille.css.InlineStyle.RuleMainBlock;
import org.daisy.braille.css.InlineStyle.RuleRelativeBlock;
import org.daisy.braille.css.InlineStyle.RuleRelativeHyphenationResource;
import org.daisy.braille.css.InlineStyle.RuleRelativePage;
import org.daisy.braille.css.InlineStyle.RuleRelativeVolume;
import org.daisy.braille.css.PropertyValue;
import org.daisy.braille.css.SelectorImpl.PseudoElementImpl;
import org.daisy.braille.css.SimpleInlineStyle;
import org.daisy.braille.css.SupportedBrailleCSS;
import org.daisy.braille.css.RuleCounterStyle;
import org.daisy.braille.css.RuleHyphenationResource;
import org.daisy.braille.css.RuleTextTransform;
import org.daisy.braille.css.RuleVolume;
import org.daisy.braille.css.RuleVolumeArea;
import org.daisy.braille.css.VendorAtRule;
import org.daisy.pipeline.braille.css.impl.BrailleCssParser.ParsedDeclaration;
import org.daisy.pipeline.braille.css.impl.BrailleCssParser.ParsedDeclarations;
import org.daisy.pipeline.css.CounterStyle;
import org.w3c.dom.Element;
/**
* Tree reprentation of a CSS style. The tree structure is based on Sass' nesting of selectors.
*
* This class is immutable
*/
public final class BrailleCssStyle implements Cloneable {
public final static BrailleCssStyle EMPTY = new Builder().build();
// these fields need to be package private because they are used in BrailleCssSerializer and BrailleCssParser
// note that even though the declarations are assumed to not change, we don't assume they are unmodifiable
Iterable extends Declaration> declarations;
SortedMap nestedStyles; // sorted by key
// used in BrailleCssSerializer
BrailleCssParser parser;
Context context;
public final Object underlyingObject; // - CounterStyle
// - null
private BrailleCssStyle(Builder builder) {
this.underlyingObject = builder.underlyingObject;
if ((builder.parser != null && builder.parser.getSupportedBrailleCSS(builder.context).isPresent())
|| builder.declarations != null && Iterables.any(builder.declarations.values(),
d -> d instanceof PropertyValue)) {
this.declarations = new ParsedDeclarations(builder.parser,
builder.context,
builder.declarations != null
? builder.declarations.values()
: null);
this.parser = ((ParsedDeclarations)this.declarations).getParser();
this.context = ((ParsedDeclarations)this.declarations).getContext();
} else {
if (builder.declarations == null || builder.declarations.isEmpty())
this.declarations = null;
else
this.declarations = ImmutableList.copyOf(builder.declarations.values());
this.parser = builder.parser;
this.context = builder.context;
}
this.nestedStyles = builder.nestedStyles != null && !builder.nestedStyles.isEmpty()
? ImmutableSortedMap.copyOfSorted(builder.buildNestedStyles())
: null;
}
public boolean isEmpty() {
if (nestedStyles != null)
return false;
if (declarations instanceof ParsedDeclarations)
// a ParsedDeclarations is never really empty because it contains information about initial values
return false;
if (declarations == null)
return true;
if (Iterables.isEmpty(declarations))
return true;
return false;
}
private boolean evaluated = false;
/**
* Evaluate attr() and content() values in content and
* string-set properties.
*/
public BrailleCssStyle evaluate(Element context) {
if (evaluated) return this;
BrailleCssStyle copy = null;
if (declarations != null) {
Iterable extends Declaration> evaluatedDeclarations = evaluateDeclarations(context);
if (copy == null && evaluatedDeclarations != declarations)
copy = clone();
if (copy != null) {
copy.declarations = evaluatedDeclarations;
copy.declarationMap = null;
}
}
if (nestedStyles != null) {
SortedMap nestedStylesCopy = new TreeMap<>(sortSelectors);
for (Map.Entry e : nestedStyles.entrySet()) {
BrailleCssStyle nestedStyle = e.getValue();
BrailleCssStyle nestedStyleEvaluated = nestedStyle.evaluate(context);
if (copy == null && nestedStyleEvaluated != nestedStyle)
copy = clone();
nestedStylesCopy.put(e.getKey(), nestedStyleEvaluated);
}
if (copy != null)
copy.nestedStyles = nestedStylesCopy;
}
if (copy != null) {
copy.serialized = null;
copy.evaluated = true;
return copy;
} else
return this;
}
private Iterable extends Declaration> evaluateDeclarations(Element context) {
Iterable extends Declaration> copy = null;
if (declarations != null)
for (Declaration d : declarations) {
if (d instanceof PropertyValue) {
PropertyValue pv = (PropertyValue)d;
CSSProperty p = pv.getCSSProperty();
if (p == Content.content_list) {
Term> v = pv.getValue();
if (v instanceof ContentList) {
// clone because evaluate() mutates the value
if (copy == null)
copy = copyDeclarations();
// find value in cloned declarations
for (Declaration dd : copy)
if (((PropertyValue)dd).getCSSProperty() == Content.content_list) {
((ContentList)((PropertyValue)dd).getValue()).evaluate(context);
break; }
} else
throw new IllegalStateException(); // coding error
} else if (p == StringSet.list_values) {
Term> v = pv.getValue();
if (v instanceof StringSetList) {
// clone because evaluate() mutates the value
if (copy == null)
copy = copyDeclarations();
// find value in cloned declarations
for (Declaration dd : copy)
if (((PropertyValue)dd).getCSSProperty() == StringSet.list_values) {
((StringSetList)((PropertyValue)dd).getValue()).evaluate(context);
break; }
} else
throw new IllegalStateException(); // coding error
}
}
}
if (copy != null)
return copy;
else
return declarations;
}
// also used in BrailleCssParser
static Declaration evaluateDeclaration(Declaration declaration, Element context) {
if (!(declaration instanceof PropertyValue))
return declaration;
PropertyValue pv = (PropertyValue)declaration;
PropertyValue copy = null;
CSSProperty p = pv.getCSSProperty();
if (p == Content.content_list) {
if (pv.getValue() instanceof ContentList) {
// clone because evaluate() mutates the value
copy = (PropertyValue)declaration.clone();
((ContentList)copy.getValue()).evaluate(context);
} else
throw new IllegalStateException(); // coding error
} else if (p == StringSet.list_values) {
if (pv.getValue() instanceof StringSetList) {
// clone because evaluate() mutates the value
copy = (PropertyValue)declaration.clone();
((StringSetList)copy.getValue()).evaluate(context);
} else
throw new IllegalStateException(); // coding error
}
if (copy != null)
return copy;
else
return declaration;
}
/**
* @return a deep copy of the {@code declarations} field
*/
private Iterable extends Declaration> copyDeclarations() {
if (declarations instanceof ParsedDeclarations)
// make sure that declarations field stays a ParsedDeclarations object because
// it is assumed throughout the code
return (ParsedDeclarations)((ParsedDeclarations)declarations).clone();
else {
List declarationsCopy = new ArrayList<>();
for (Declaration dd : declarations)
declarationsCopy.add((Declaration)dd.clone());
return ImmutableList.copyOf(declarationsCopy);
}
}
private Map declarationMap = null;
private Map getDeclarationMap() {
if (declarations != null) {
synchronized (this) {
if (declarationMap == null) {
declarationMap = new TreeMap<>();
for (Declaration d : declarations)
declarationMap.put(d.getProperty(), d);
}
}
}
return declarationMap;
}
public Iterable getPropertyNames() {
if (declarations != null)
return getDeclarationMap().keySet();
else
return Collections.emptyList();
}
/**
* Caller must guarantee that the {@link Declaration} objects will not be modified.
*/
public Iterable getDeclarations() {
if (declarations != null)
return getDeclarationMap().values();
else
return Collections.emptyList();
}
public Iterable getSelectors() {
if (nestedStyles != null)
return nestedStyles.keySet();
else
return Collections.emptyList();
}
private List rules = null;
public Iterable getRules() {
if (nestedStyles != null) {
synchronized (this) {
if (rules == null) {
rules = new ArrayList<>();
for (Map.Entry e : nestedStyles.entrySet()) {
String selector = e.getKey();
rules.add(new Builder().add(selector, e.getValue()).build());
}
}
}
return rules;
} else
return Collections.emptyList();
}
/**
* Return the style as a {@link SimpleInlineStyle} object.
*
* @param mutable Whether the caller wishes to mutate the returned object.
* @throws UnsupportedOperationException if this is not a simple inline style
*/
public SimpleInlineStyle asSimpleInlineStyle(boolean mutable) {
if (nestedStyles != null)
throw new UnsupportedOperationException("not a simple inline style");
else if (declarations == null)
return SimpleInlineStyle.EMPTY;
else if (context != Context.ELEMENT)
throw new UnsupportedOperationException("not a simple inline style");
else if (!(declarations instanceof ParsedDeclarations))
throw new IllegalStateException(); // coding error
ParsedDeclarations s = (ParsedDeclarations)declarations;
if (mutable && !s.isEmpty()) {
s = (ParsedDeclarations)s.clone(); // make a deep copy
for (Declaration d : s) {
if (d instanceof PropertyValue) {
PropertyValue pv = (PropertyValue)d;
CSSProperty p = pv.getCSSProperty();
if (p == TextTransform.list_values) {
Term> v = pv.getValue();
if (v instanceof TextTransformList)
((TextTransformList)v).locked = false;
break;
}
}
}
// mark as mutable
s.locked = false;
}
return s;
}
// used in BrailleCssParser
static Declaration unlockDeclaration(Declaration declaration) {
if (!(declaration instanceof PropertyValue))
return declaration;
PropertyValue pv = (PropertyValue)declaration;
if (pv.getCSSProperty() == TextTransform.list_values) {
if (pv.getValue() instanceof TextTransformList) {
// clone because we're going to make the value mutable
pv = (PropertyValue)pv.clone();
((TextTransformList)pv.getValue()).locked = false;
} else
throw new IllegalStateException(); // coding error
}
return pv;
}
/**
* Get the declaration for the given property name.
*
* Caller must guarantee that the object will not be modified.
*/
public Declaration getDeclaration(String propertyName) {
return getDeclaration(propertyName, false);
}
/**
* @param includeInitial Whether to include the initial value if the style does not contain the property.
*/
public Declaration getDeclaration(String propertyName, boolean includeInitial) {
if (declarations != null) {
Declaration d = getDeclarationMap().get(propertyName);
if (d != null)
return d;
}
if (includeInitial && declarations instanceof ParsedDeclarations)
return ((ParsedDeclarations)declarations).getOrDefault(propertyName);
return null;
}
/**
* Get the nested style for the given selector.
*/
public BrailleCssStyle getNestedStyle(String selector) {
if (nestedStyles != null) {
BrailleCssStyle n = nestedStyles.get(selector);
if (n != null)
return n;
}
if (parser != null)
switch (context) {
case ELEMENT:
// return empty @page rule rather than null, in order to make it possible to
// get default page property values using s:getOrDefault()
if ("@page".equals(selector))
// by passing parser this object will not be empty (because it contains
// information about initial values, see #isEmpty())
return new Builder(parser, Context.PAGE).build();
break;
default:
break;
}
return null;
}
/**
* Remove declaration if key is a property name, or nested style if key is a selector.
*/
public BrailleCssStyle remove(String key) {
if (getDeclaration(key) != null ||
nestedStyles != null && nestedStyles.containsKey(key))
return new Builder(this).remove(key).build();
else
return this;
}
/**
* For each provided key, remove declaration if key is a property name, or rule if key is a selector.
*/
public BrailleCssStyle remove(Iterator keys) {
if (keys.hasNext()) {
Builder b = new Builder(this);
while (keys.hasNext()) {
String key = keys.next();
if (getDeclaration(key) != null ||
nestedStyles != null && nestedStyles.containsKey(key))
b = b.remove(key);
}
return b.build();
}
return this;
}
/**
* Add other styles to this style. Properties are overwritten by properties declared in
* following style items.
*
* @param styles objects must be of type {@link BrailleCssStyle} or {@link Declaration}.
*/
public BrailleCssStyle add(Iterator