w3c.css.properties.css3.CssBackgroundPosition Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cssvalidator Show documentation
Show all versions of cssvalidator Show documentation
Backend for the W3C CSS Validation Service
// $Id$
// From Philippe Le Hegaret ([email protected])
// Rewritten by Yves Lafon
// (c) COPYRIGHT MIT, Keio and ERCIM, 1997-2010.
// Please first read the full copyright statement in file COPYRIGHT.html
package org.w3c.css.properties.css3;
import org.w3c.css.util.ApplContext;
import org.w3c.css.util.InvalidParamException;
import org.w3c.css.values.CssExpression;
import org.w3c.css.values.CssIdent;
import org.w3c.css.values.CssLayerList;
import org.w3c.css.values.CssPercentage;
import org.w3c.css.values.CssTypes;
import org.w3c.css.values.CssValue;
import org.w3c.css.values.CssValueList;
import java.util.ArrayList;
import static org.w3c.css.values.CssOperator.COMMA;
import static org.w3c.css.values.CssOperator.SPACE;
/**
* @spec http://www.w3.org/TR/2009/CR-css3-background-20091217/#background-position
*/
public class CssBackgroundPosition extends org.w3c.css.properties.css.CssBackgroundPosition {
public static CssIdent[] allowed_values;
public static CssIdent center, top, bottom, left, right;
private static CssPercentage defaultPercent0, defaultPercent50;
private static CssPercentage defaultPercent100;
static {
top = CssIdent.getIdent("top");
bottom = CssIdent.getIdent("bottom");
left = CssIdent.getIdent("left");
right = CssIdent.getIdent("right");
center = CssIdent.getIdent("center");
allowed_values = new CssIdent[5];
allowed_values[0] = top;
allowed_values[1] = bottom;
allowed_values[2] = left;
allowed_values[3] = right;
allowed_values[4] = center;
defaultPercent0 = new CssPercentage(0);
defaultPercent50 = new CssPercentage(50);
defaultPercent100 = new CssPercentage(100);
}
public static boolean isMatchingIdent(CssIdent ident) {
for (CssIdent id : allowed_values) {
if (id.equals(ident)) {
return true;
}
}
return false;
}
public static CssIdent getMatchingIdent(CssIdent ident) {
for (CssIdent id : allowed_values) {
if (id.equals(ident)) {
return id;
}
}
return null;
}
/**
* Create a new CssBackgroundPosition
*/
public CssBackgroundPosition() {
value = new CssBackgroundPositionValue();
}
/**
* Creates a new CssBackgroundPosition
*
* @param expression The expression for this property
* @throws org.w3c.css.util.InvalidParamException
* Values are incorrect
*/
public CssBackgroundPosition(ApplContext ac, CssExpression expression,
boolean check) throws InvalidParamException {
setByUser();
CssValue val;
ArrayList values;
CssBackgroundPositionValue b_val = null;
char op;
values = new ArrayList();
// we just accumulate values and check at validation
while (!expression.end()) {
val = expression.getValue();
op = expression.getOperator();
if (inherit.equals(val)) {
if (expression.getCount() > 1) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
value = inherit;
expression.next();
return;
}
if (b_val == null) {
b_val = new CssBackgroundPositionValue();
}
// we will check later
b_val.add(val);
expression.next();
if (!expression.end()) {
// incomplete value followed by a comma... it's complete!
if (op == COMMA) {
check(b_val, ac);
values.add(b_val);
b_val = null;
} else if (op != SPACE) {
throw new InvalidParamException("operator",
((new Character(op)).toString()), ac);
}
}
}
// if we reach the end in a value that can come in pair
if (b_val != null) {
check(b_val, ac);
values.add(b_val);
}
if (values.size() == 1) {
value = values.get(0);
} else {
value = new CssLayerList(values);
}
}
public CssBackgroundPosition(ApplContext ac, CssExpression expression)
throws InvalidParamException {
this(ac, expression, false);
}
/**
* Is the value of this property is a default value.
* It is used by all macro for the function print
*/
public boolean isDefault() {
if (!(value instanceof CssBackgroundPositionValue)) {
return false;
}
CssBackgroundPositionValue v = (CssBackgroundPositionValue) value;
return ((v.val_vertical == defaultPercent0) &&
(v.val_horizontal == defaultPercent0) &&
(v.vertical_offset == null) &&
(v.horizontal_offset == null));
}
public void check(CssBackgroundPositionValue v, ApplContext ac)
throws InvalidParamException {
int nb_keyword = 0;
int nb_percentage = 0;
int nb_length = 0;
int nb_values = v.value.size();
if (nb_values > 4) {
throw new InvalidParamException("unrecognize", ac);
}
// basic check
for (CssValue aValue : v.value) {
switch (aValue.getType()) {
case CssTypes.CSS_NUMBER:
aValue.getLength();
case CssTypes.CSS_LENGTH:
nb_length++;
break;
case CssTypes.CSS_PERCENTAGE:
nb_percentage++;
break;
case CssTypes.CSS_IDENT:
nb_keyword++;
break;
default:
throw new InvalidParamException("unrecognize", aValue,
ac);
}
}
if ((nb_keyword > 2) || (nb_length > 2) || (nb_percentage > 2)) {
throw new InvalidParamException("unrecognize", ac);
}
// this is unnecessary complex, blame it on the CSS3 spec.
switch (nb_keyword) {
case 0:
// no keyword, so it's easy, it depends on the number
// of values :)
switch (nb_values) {
case 1:
// If only one value is specified, the second value
// is assumed to be 'center'.
v.horizontal = v.value.get(0);
if (v.horizontal.getType() == CssTypes.CSS_NUMBER) {
v.horizontal = defaultPercent0;
}
v.val_horizontal = v.horizontal;
v.val_vertical = defaultPercent50;
break;
case 2:
v.horizontal = v.value.get(0);
if (v.horizontal.getType() == CssTypes.CSS_NUMBER) {
v.horizontal = defaultPercent0;
}
v.val_horizontal = v.horizontal;
v.vertical = v.value.get(1);
if (v.vertical.getType() == CssTypes.CSS_NUMBER) {
v.vertical = defaultPercent0;
}
v.val_vertical = v.vertical;
break;
default:
// If three or four values are given, then each
// or represents an offset and
// must be preceded by a keyword
throw new InvalidParamException("unrecognize",
ac);
}
break;
// we got one keyword... let's have fun...
case 1:
switch (nb_values) {
case 1:
CssIdent ident = (CssIdent) v.value.get(0);
// ugly as we need to set values for equality tests
v.val_vertical = defaultPercent50;
v.val_horizontal = defaultPercent50;
ident = getMatchingIdent(ident);
if (ident != null) {
if (isVertical(ident)) {
v.vertical = ident;
v.val_vertical = identToPercent(ident);
} else {
// horizontal || center
v.horizontal = ident;
v.val_horizontal = identToPercent(ident);
}
break;
}
throw new InvalidParamException("unrecognize",
ident, getPropertyName(), ac);
case 2:
// one ident, two values... first MUST be horizontal
// and second vertical
CssValue val0 = v.value.get(0);
if (val0.getType() == CssTypes.CSS_IDENT) {
ident = getMatchingIdent((CssIdent) val0);
if (ident == null) {
throw new InvalidParamException("unrecognize",
ident, getPropertyName(), ac);
}
if (isVertical(ident)) {
throw new InvalidParamException("incompatible",
ident, v.value.get(1), ac);
}
v.horizontal = ident;
v.val_horizontal = identToPercent(ident);
// and the vertical value...
v.vertical = v.value.get(1);
if (v.vertical.getType() == CssTypes.CSS_NUMBER) {
v.vertical = defaultPercent0;
}
v.val_vertical = v.vertical;
} else {
CssValue value1 = v.value.get(1);
if (value1.getType() != CssTypes.CSS_IDENT) {
throw new InvalidParamException("unrecognize",
value1, getPropertyName(), ac);
}
ident = getMatchingIdent((CssIdent) value1);
if (ident == null) {
throw new InvalidParamException("unrecognize",
ident, getPropertyName(), ac);
}
if (isHorizontal(ident)) {
throw new InvalidParamException("incompatible",
val0, value1, ac);
}
v.vertical = ident;
v.val_vertical = identToPercent(ident);
// and the first value
v.horizontal = val0;
if (v.horizontal.getType() == CssTypes.CSS_NUMBER) {
v.horizontal = defaultPercent0;
}
v.val_horizontal = v.horizontal;
}
break;
default:
// one ident, 3 or 4 values is not allowed
throw new InvalidParamException("unrecognize",
ac);
}
break;
default:
// ok so we have two keywords, with possible offsets
// we must check that every possible offset is right
// after a keyword and also that the two keywords are
// not incompatible
boolean got_ident = false;
CssIdent id1 = null;
CssIdent id2 = null;
CssValue off1 = null;
CssValue off2 = null;
for (CssValue aValue : v.value) {
switch (aValue.getType()) {
case CssTypes.CSS_IDENT:
aValue = getMatchingIdent((CssIdent) aValue);
if (aValue == null) {
throw new InvalidParamException("unrecognize",
aValue, getPropertyName(), ac);
}
got_ident = true;
if (id1 == null) {
id1 = (CssIdent) aValue;
} else {
id2 = (CssIdent) aValue;
// we got both, let's check.
if (((isVertical(id1) && isVertical(id2))) ||
(isHorizontal(id1) && isHorizontal(id2))) {
throw new InvalidParamException("incompatible",
id1, id2, ac);
}
}
break;
case CssTypes.CSS_NUMBER:
aValue = aValue.getLength();
case CssTypes.CSS_PERCENTAGE:
case CssTypes.CSS_LENGTH:
if (!got_ident) {
throw new InvalidParamException("unrecognize",
aValue, getPropertyName(), ac);
}
if (id2 == null) {
off1 = aValue;
} else {
off2 = aValue;
}
got_ident = false;
break;
default:
// should never happen
}
}
if (isVertical(id1) || isHorizontal(id2)) {
// if an offset is present and value is 'center'
if (((off1 != null) && !isVertical(id1)) ||
((off2 != null) && !isHorizontal(id2))) {
throw new InvalidParamException("incompatible",
id1, id2, ac);
}
v.horizontal = id2;
v.val_horizontal = identToPercent(id2);
v.horizontal_offset = off2;
v.vertical = id1;
v.val_vertical = identToPercent(id1);
v.vertical_offset = off1;
} else {
if (((off2 != null) && !isVertical(id2)) ||
((off1 != null) && !isHorizontal(id1))) {
throw new InvalidParamException("incompatible",
id1, id2, ac);
}
v.horizontal = id1;
v.val_horizontal = identToPercent(id1);
v.horizontal_offset = off1;
v.vertical = id2;
v.val_vertical = identToPercent(id2);
v.vertical_offset = off2;
}
}
}
public static CssPercentage identToPercent(CssIdent ident) {
if (center.equals(ident)) {
return defaultPercent50;
} else if (top.equals(ident) || left.equals(ident)) {
return defaultPercent0;
} else if (bottom.equals(ident) || right.equals(ident)) {
return defaultPercent100;
}
return defaultPercent0; // FIXME throw an exception ?
}
public static boolean isHorizontal(CssIdent ident) {
return (left.equals(ident) || right.equals(ident));
}
public static boolean isVertical(CssIdent ident) {
return (top.equals(ident) || bottom.equals(ident));
}
public static CssValue checkSyntax(CssExpression expression, ApplContext ac, String caller)
throws InvalidParamException {
ArrayList v = new ArrayList();
int nb_values = expression.getCount();
CssValue val, ret;
int nb_length, nb_percentage, nb_keyword;
char op;
nb_length = nb_percentage = nb_keyword = 0;
while (!expression.end()) {
val = expression.getValue();
op = expression.getOperator();
switch (val.getType()) {
case CssTypes.CSS_NUMBER:
val.getLength();
case CssTypes.CSS_LENGTH:
nb_length++;
break;
case CssTypes.CSS_PERCENTAGE:
nb_percentage++;
break;
case CssTypes.CSS_IDENT:
nb_keyword++;
break;
default:
throw new InvalidParamException("unrecognize", val,
ac);
}
v.add(val);
if (op != SPACE) {
throw new InvalidParamException("operator",
((new Character(op)).toString()), ac);
}
expression.next();
}
ret = new CssValueList(v);
// basic checks
if ((nb_keyword > 2) || (nb_length > 2) || (nb_percentage > 2)) {
throw new InvalidParamException("value", ret,
caller, ac);
}
// test based on the number of keywords
switch (nb_keyword) {
case 0:
if (nb_keyword > 2) {
throw new InvalidParamException("value", ret,
caller, ac);
}
break;
case 1:
switch (nb_values) {
case 1:
// one value, one keyword... easy :)
if (getMatchingIdent((CssIdent) v.get(0)) == null) {
throw new InvalidParamException("value", v.get(0),
caller, ac);
}
break;
case 2:
// one ident, two values... first MUST be horizontal
// and second vertical
CssValue val0 = v.get(0);
CssIdent ident;
if (val0.getType() == CssTypes.CSS_IDENT) {
ident = getMatchingIdent((CssIdent) val0);
if (ident == null) {
throw new InvalidParamException("value", val0,
caller, ac);
}
if (isVertical(ident)) {
throw new InvalidParamException("incompatible",
ident, v.get(1), ac);
}
} else {
CssValue value1 = v.get(1);
if (value1.getType() != CssTypes.CSS_IDENT) {
throw new InvalidParamException("value", value1,
caller, ac);
}
ident = getMatchingIdent((CssIdent) value1);
if (ident == null) {
throw new InvalidParamException("value", value1,
caller, ac);
}
if (isHorizontal(ident)) {
throw new InvalidParamException("incompatible",
val0, value1, ac);
}
}
break;
default:
// one ident, 3 or 4 values is not allowed
throw new InvalidParamException("value", ret,
caller, ac);
}
break;
default:
// ok so we have two keywords, with possible offsets
// we must check that every possible offset is right
// after a keyword and also that the two keywords are
// not incompatible
boolean got_ident = false;
CssIdent id1 = null;
CssIdent id2 = null;
CssValue off1 = null;
CssValue off2 = null;
for (CssValue aValue : v) {
switch (aValue.getType()) {
case CssTypes.CSS_IDENT:
if (getMatchingIdent((CssIdent) aValue) == null) {
throw new InvalidParamException("value", aValue,
caller, ac);
}
got_ident = true;
if (id1 == null) {
id1 = (CssIdent) aValue;
} else {
id2 = (CssIdent) aValue;
// we got both, let's check.
if (((isVertical(id1) && isVertical(id2))) ||
(isHorizontal(id1) && isHorizontal(id2))) {
throw new InvalidParamException("incompatible",
id1, id2, ac);
}
}
break;
case CssTypes.CSS_NUMBER:
case CssTypes.CSS_PERCENTAGE:
case CssTypes.CSS_LENGTH:
if (!got_ident) {
throw new InvalidParamException("unrecognize",
aValue, caller, ac);
}
if (id2 == null) {
off1 = aValue;
} else {
off2 = aValue;
}
got_ident = false;
break;
default:
// should never happen
}
}
if (isVertical(id1) || isHorizontal(id2)) {
// if an offset is present and value is 'center'
if (((off1 != null) && !isVertical(id1)) ||
((off2 != null) && !isHorizontal(id2))) {
throw new InvalidParamException("incompatible",
id1, id2, ac);
}
} else {
if (((off2 != null) && !isVertical(id2)) ||
((off1 != null) && !isHorizontal(id1))) {
throw new InvalidParamException("incompatible",
id1, id2, ac);
}
}
}
return ret;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy