w3c.css.properties.css3.CssBackgroundPosition Maven / Gradle / Ivy
// $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.properties.css.CssProperty;
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 checkBackgroundPosition(ApplContext ac, CssExpression expression,
CssProperty caller)
throws InvalidParamException {
return checkSyntax(ac, expression, caller.getPropertyName());
}
public static CssValue checkSyntax(ApplContext ac, CssExpression expression, 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