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 cz.vutbr.web.domassign.decode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import cz.vutbr.web.css.CSSFactory;
import cz.vutbr.web.css.CSSProperty;
import cz.vutbr.web.css.CSSProperty.ValueType;
import cz.vutbr.web.css.Declaration;
import cz.vutbr.web.css.SupportedCSS;
import cz.vutbr.web.css.Term;
import cz.vutbr.web.css.TermIdent;
import cz.vutbr.web.css.TermList;
import cz.vutbr.web.css.TermPropertyValue;
import cz.vutbr.web.css.Term.Operator;
/**
* Selects appropriate variant when parsing content of CSS declaration. Allows
* easy parsing of CSS declaration multi-values such as
* border: blue 1px
*
* @author kapy
*
*/
public abstract class Variator extends Decoder {
/**
* All variants flag
*/
protected final static int ALL_VARIANTS = -1;
/**
* Total variants available
*/
protected int variants;
/**
* Results of variants. Each variant is allowed to be passed only once in
* case of multi-value declaration, so this array is used to show that
* currently passed variant was already successfully passed in past
*/
protected boolean[] variantPassed;
/**
* Property names according to each variant
*/
protected List names;
protected List> types;
/**
* Terms over which variants are tested
*/
protected List> terms;
/**
* Creates variator which contains variants variants to be
* tested
*
* @param variants
*/
public Variator(int variants) {
this.variants = variants;
this.variantPassed = new boolean[variants];
this.names = new ArrayList(variants);
this.types = new ArrayList>(variants);
reset();
}
/**
* Resets the variator to its initial state.
*/
public void reset() {
for (int i = 0; i < variants; i++)
variantPassed[i] = false;
}
/**
* This function contains parsing block for variants
*
* @param variant
* Tested variant
* @param iteration
* Number of iteration, that is term to be tested.
* This number may be changed internally in function
* to inform that more than one term was used for variant
* @param properties
* Properties map where to store properties types
* @param values
* Values map where to store properties values
* @return true in case of success, false
* otherwise
*/
protected abstract boolean variant(int variant, IntegerRef iteration,
Map properties, Map> values);
/**
* Solves variant which leads to inherit CSS Property value.
* This overrides all other possible variants and no other informations are
* allowed per CSS Declaration.
*
* This method is called before check for variants or before variant itself
* is called in one shot way.
*
* Example: margin: inherit is valid value and leads to setting
* of
*
*
margin-top: inherit
*
margin-right: inherit
*
margin-bottom: inherit
*
margin-left: inherit
*
*
* margin: 0px inherit is invalid value.
*
* @param variant
* Number of variant or identifier of all variants
* VARIANT_ALL
* @param properties
* Properties map where to store properties types
* @param term
* Term to be checked
* @return true in case of success, false
* otherwise
*/
protected boolean checkInherit(int variant, Term> term,
Map properties) {
// check whether term equals inherit
if (!(term instanceof TermIdent)
|| !CSSProperty.INHERIT_KEYWORD
.equalsIgnoreCase(((TermIdent) term).getValue())) {
return false;
}
if (variant == ALL_VARIANTS) {
for (int i = 0; i < variants; i++) {
properties.put(names.get(i), createInherit(i));
}
return true;
}
properties.put(names.get(variant), createInherit(variant));
return true;
}
/**
* Creates INHERIT value of given class
*
* @param i
* Ordinal in list of types
* @return Created CSSProperty with value inherit
* @throws UnsupportedOperationException
* If class does not provide INHERIT or is not implementation of
* CSSProperty
*/
private CSSProperty createInherit(int i) {
try {
Class extends CSSProperty> clazz = types.get(i);
CSSProperty property = CSSProperty.Translator.createInherit(clazz);
if (property != null)
return property;
throw new IllegalAccessException("No inherit value for: "
+ clazz.getName());
} catch (Exception e) {
throw new UnsupportedOperationException(
"Unable to create inherit value", e);
}
}
/**
* Check if variant, which was passed is able to be located in place where it was
* found.
*
* Example:
* We have declaration:
* font: 12px/14px sans-serif
* Then according to grammar:
*
14px is checked to have SLASH operator before
* and check to whether font-size was defined before it
*
sans-serif is tested to have at least
* definition of font-size before itself
*
declaration passes
*
*
* @param variant Identification of current variant which passed test
* @param term Position in term list of terms which passed test, for multiple
* value term allow to change it
* @return true in case of success, false elsewhere
* @see Term.Operator
*/
protected boolean variantCondition(int variant, IntegerRef term) {
return true;
}
/**
* Test all terms
*
*/
public boolean vary(Map properties,
Map> values) {
// try inherit variant
if (terms.size() == 1
&& checkInherit(ALL_VARIANTS, terms.get(0), properties))
return true;
// for all terms
for (IntegerRef i = new IntegerRef(0); i.get() < terms.size(); i.inc()) {
boolean passed = false;
// check all variants
for (int v = 0; v < variants; v++) {
// check and if variant was already found
// signalize error by discarding complete declaration
// have to check variant condition firstly to avoid false
// positive
// variantPassed
if (!variantCondition(v, i))
continue;
//if this variant already passed, do not try again
//TODO: check if we shouldn't try better combination of terms
if (variantPassed[v])
continue;
//check if this term corresponds to this variant
passed = variant(v, i, properties, values);
if (passed) {
// mark occurrence of variant
variantPassed[v] = true;
// we have found, skip evaluation
break;
}
}
// no variant could be assigned
if (!passed)
return false;
}
// all terms passed
return true;
}
/**
* Uses variator functionality to test selected variant on term
*
* @param variant
* Which variant will be tested
* @param d
* The declaration on which variant will be tested
* @param properties
* Properties map where to store property type
* @param values
* Values map where to store property value
* @return true in case of success, false
* otherwise
*/
public boolean tryOneTermVariant(int variant, Declaration d,
Map properties, Map> values) {
// only one term is allowed
if (d.size() != 1)
return false;
// try inherit variant
if (checkInherit(variant, d.get(0), properties))
return true;
this.terms = new ArrayList>();
this.terms.add(d.get(0));
return variant(variant, new IntegerRef(0), properties, values);
}
/**
* Uses variator functionality to test selected variant on more terms. This
* is used when variant is represented by more terms. Since usually only one
* term per variant is used, only one multiple variant is allowed per
* variator and should be placed as the last one
*
* @param variant
* Number of variant (last variant in variator)
* @param properties
* Properties map where to store property type
* @param values
* Values map where to store property value
* @param terms
* Array of terms used for variant
* @return true in case of success, false
* otherwise
*/
public boolean tryMultiTermVariant(int variant,
Map properties, Map> values,
Term>... terms) {
this.terms = Arrays.asList(terms);
// try inherit variant
if (this.terms.size() == 1
&& checkInherit(variant, this.terms.get(0), properties))
return true;
return variant(variant, new IntegerRef(0), properties, values);
}
/**
* Assigns property names for each variant
*
* @param variantPropertyNames
* List of property names
*/
public void assignVariantPropertyNames(String... variantPropertyNames) {
this.names = Arrays.asList(variantPropertyNames);
}
/**
* Assigns terms to be checked by variator
*
* @param terms
* Terms to be assigned
*/
public void assignTerms(Term>... terms) {
this.terms = Arrays.asList(terms);
}
/**
* Assigns terms from declaration
*
* @param d
* Declaration which contains terms
*/
public void assignTermsFromDeclaration(Declaration d) {
this.terms = d.asList();
}
/**
* Assigns the default values to all the properties.
* @param properties
* @param values
*/
public void assignDefaults(Map properties, Map> values) {
SupportedCSS css = CSSFactory.getSupportedCSS();
for (String name : names) {
CSSProperty dp = css.getDefaultProperty(name);
if (dp != null)
properties.put(name, dp);
Term> dv = css.getDefaultValue(name);
if (dv != null)
values.put(name, dv);
}
}
//=========================================================================
// Nested list creation
/**
* Tries a single variant that may consist of a term or a list of comma-separated terms.
*
* @param variant the variant to be tried
* @param d the declaration to be processed
* @param properties target property map
* @param values target value map
* @param listValue the list_values value to be used for the property value
* @return {@code true} when parsed successfully
*/
public boolean tryListOfOneTermVariant(int variant, Declaration d,
Map properties, Map> values,
CSSProperty listValue) {
// try inherit variant
if (d.size() == 1 && checkInherit(variant, d.get(0), properties))
return true;
//scan the list
TermList list = tf.createList();
final Map p = new HashMap<>();
final Map> v = new HashMap<>();
boolean first = true;
for (Term> t : d.asList()) {
//terms must be separated by commas
if ((first && t.getOperator() != null)
|| (!first && t.getOperator() != Operator.COMMA))
return false;
//process the variant for a single term
p.clear();
v.clear();
this.terms = new ArrayList>();
this.terms.add(t);
if (!variant(variant, new IntegerRef(0), p, v))
return false;
//collect the resulting term
final CSSProperty prop = p.values().iterator().next();
final Term> val = (v.values().isEmpty()) ? null : v.values().iterator().next();
final TermPropertyValue pval = tf.createPropertyValue(prop, val);
if (!first)
pval.setOperator(Operator.COMMA);
list.add(pval);
first = false;
}
//store the result
properties.put(names.get(variant), listValue);
values.put(names.get(variant), list);
return true;
}
/**
* Tries a single variant that may consist of space-separated terms or a comma-separated list
* of space-separated lists.
*
* @param variant the variant to be tried
* @param d the declaration to be processed
* @param properties target property map
* @param values target value map
* @param listValue the list_values value to be used for the property value
* @return {@code true} when parsed successfully
*/
public boolean tryListOfMultiTermVariant(int variant, Declaration d,
Map properties, Map> values,
CSSProperty listValue) {
// try inherit variant
if (d.size() == 1 && checkInherit(variant, d.get(0), properties))
return true;
// for all sub-declarations
TermList list = tf.createList();
final Map p = new HashMap<>();
final Map> v = new HashMap<>();
boolean first = true;
List subs = splitDeclarations(d, Operator.COMMA);
for (Declaration sub : subs) {
p.clear();
v.clear();
assignTermsFromDeclaration(sub);
if (!variant(variant, new IntegerRef(0), p, v))
return false;
final CSSProperty prop = p.values().iterator().next();
final Term> val = (v.values().isEmpty()) ? null : v.values().iterator().next();
final TermPropertyValue pval = tf.createPropertyValue(prop, val);
if (!first)
pval.setOperator(Operator.COMMA);
list.add(pval);
first = false;
}
//store the result
properties.put(names.get(variant), listValue);
values.put(names.get(variant), list);
return true;
}
/**
* Varies over a comma-separated list of layers where each layer defines all the variants.
*
* @param d
* @param properties
* @param values
* @return
*/
public boolean varyList(Declaration d, Map properties,
Map> values) {
// try inherit variant
if (d.size() == 1
&& checkInherit(ALL_VARIANTS, d.get(0), properties))
return true;
// temporary result of the whole list which will be used after final validation
final Map destProps = new HashMap<>();
final Map> destVals = new HashMap<>();
// for all sub-declarations
int listIndex = 0;
List subs = splitDeclarations(d, Operator.COMMA);
for (Declaration sub : subs) {
reset();
final Map props = new HashMap<>();
final Map> vals = new HashMap<>();
assignDefaults(props, vals);
assignTermsFromDeclaration(sub);
// for all terms
for (IntegerRef i = new IntegerRef(0); i.get() < terms.size(); i.inc()) {
boolean passed = false;
// check all variants
for (int v = 0; v < variants; v++) {
// check and if variant was already found
// signalize error by discarding complete declaration
// have to check variant condition firstly to avoid false
// positive
// variantPassed
if (!variantCondition(v, i))
continue;
//if this variant already passed, do not try again
//TODO: check if we shouldn't try better combination of terms
if (variantPassed[v])
continue;
//check if this term corresponds to this variant
passed = variant(v, i, props, vals);
if (passed) {
// mark occurrence of variant
variantPassed[v] = true;
// we have found, skip evaluation
break;
}
}
// no variant could be assigned
if (!passed)
return false;
}
if (!validateListItem(listIndex, subs.size(), props, vals))
return false; //validation failed
// all terms passed
for (String key : props.keySet()) {
final CSSProperty p = props.get(key);
final Term> v = vals.get(key);
if (p.getValueType() == ValueType.LIST) {
addToMap(destVals, key, p, v, (listIndex == 0));
destProps.put(key, CSSProperty.Translator.createNestedListValue(p.getClass()));
} else {
destProps.put(key, p);
destVals.put(key, v);
}
}
listIndex++;
}
//validate and store the whole list
if (!validateList(subs.size(), destProps, destVals))
return false;
properties.putAll(destProps);
values.putAll(destVals);
return true;
}
/**
* Adds a property-value pair to a nested list in the destination value map. Creates a new
* list when the corresponding value is not set or it is not a list.
* @param dest the destination value map
* @param key the key to use (property name)
* @param property the property value to set
* @param value an optional Term value to set
* @param first {@code true} for the first value in the list (for generating separators properly)
*/
private void addToMap(Map> dest, String key, CSSProperty property, Term> value, boolean first)
{
final Term> cur = dest.get(key);
TermList list;
if (cur instanceof TermList)
list = (TermList) cur;
else {
list = tf.createList();
dest.put(key, list);
}
//make a copy and remove the original operator from the value
Term> vvalue = (value == null) ? null : value.shallowClone();
if (vvalue != null)
vvalue.setOperator(null);
//add the pair
final TermPropertyValue pval = tf.createPropertyValue(property, vvalue);
if (!first)
pval.setOperator(Operator.COMMA);
list.add(pval);
}
protected boolean validateListItem(int listIndex, int listSize,
Map properties, Map> values) {
return true; //no validation is performed by default, this may be overriden in subclasses
}
protected boolean validateList(int listSize, Map properties,
Map> values) {
return true; //no validation is performed by default, this may be overriden in subclasses
}
//=========================================================================
/**
* Reference to integer
* @author kapy
*
*/
protected static class IntegerRef {
private int i;
public IntegerRef(int i) {
this.i = i;
}
/**
* @return the i
*/
public int get() {
return i;
}
/**
* @param i the i to set
*/
public void set(int i) {
this.i = i;
}
public void inc() {
this.i++;
}
}
}