w3c.css.properties.css3.CssBackground 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 2010 Yves Lafon
// (c) COPYRIGHT MIT, ERCIM and Keio, 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.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/2012/CR-css3-background-20120724/#the-background
* @see org.w3c.css.properties.css.CssBackgroundColor
* @see org.w3c.css.properties.css.CssBackgroundImage
* @see org.w3c.css.properties.css.CssBackgroundRepeat
* @see org.w3c.css.properties.css.CssBackgroundAttachment
* @see org.w3c.css.properties.css.CssBackgroundPosition
* @see org.w3c.css.properties.css.CssBackgroundSize
*/
public class CssBackground extends org.w3c.css.properties.css.CssBackground {
/**
* Create a new CssBackground
*/
public CssBackground() {
value = initial;
}
/**
* Set the value of the property
* Does not check the number of values
*
* @param expression The expression for this property
* @throws org.w3c.css.util.InvalidParamException
* The expression is incorrect
*/
public CssBackground(ApplContext ac, CssExpression expression)
throws InvalidParamException {
this(ac, expression, false);
}
/**
* Set the value of the property
*
* @param expression The expression for this property
* @param check set it to true to check the number of values
* @throws org.w3c.css.util.InvalidParamException
* The expression is incorrect
*/
public CssBackground(ApplContext ac, CssExpression expression,
boolean check) throws InvalidParamException {
setByUser();
CssValue val;
ArrayList values;
CssExpression single_layer = null;
CssBackgroundValue 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 (single_layer == null) {
single_layer = new CssExpression();
}
// we will check later
single_layer.addValue(val);
single_layer.setOperator(op);
expression.next();
if (!expression.end()) {
// incomplete value followed by a comma... it's complete!
if (op == COMMA) {
single_layer.setOperator(SPACE);
b_val = check(ac, single_layer, check, false);
values.add(b_val);
single_layer = 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 (single_layer != null) {
b_val = check(ac, single_layer, check, true);
values.add(b_val);
}
if (values.size() == 1) {
value = values.get(0);
} else {
value = new CssLayerList(values);
}
transform_into_individual_values();
}
private Object getCssBackgroundRepeatValue(ApplContext ac,
CssExpression expression,
boolean check)
throws InvalidParamException {
char op = expression.getOperator();
CssExpression exp = new CssExpression();
exp.addValue(expression.getValue());
repeat = new CssBackgroundRepeat(ac, exp, check);
// now check if we can add a second value ;)
if ((op == SPACE) && !expression.end()) {
expression.next();
if (!expression.end()) {
CssValue val = expression.getValue();
if ((val.getType() == CssTypes.CSS_IDENT) &&
(CssBackgroundRepeat.isMatchingIdent((CssIdent) val))) {
exp.addValue(expression.getValue());
exp.starts();
try {
repeat = new CssBackgroundRepeat(ac, exp, check);
} catch (InvalidParamException ipe) {
expression.precedent();
}
} else {
expression.precedent();
}
}
}
return repeat.get();
}
private Object getCssBackgroundSizeValue(ApplContext ac,
CssExpression expression,
boolean check)
throws InvalidParamException {
char op = expression.getOperator();
CssExpression exp = new CssExpression();
exp.addValue(expression.getValue());
CssBackgroundSize bg_size;
bg_size = new CssBackgroundSize(ac, exp, check);
// now check if we can add a second value ;)
// TODO really dirty.. must check the use of 'check'
// here, and possibly adjust the parsing model in
// other classes :(
if ((op == SPACE) && !expression.end()) {
expression.next();
if (!expression.end()) {
exp.addValue(expression.getValue());
exp.starts();
try {
bg_size = new CssBackgroundSize(ac, exp, check);
} catch (InvalidParamException ipe) {
// roll back
expression.precedent();
}
}
}
return bg_size.get();
}
private Object getCssBackgroundPositionValue(ApplContext ac,
CssExpression expression,
boolean check)
throws InvalidParamException {
CssExpression exp = new CssExpression();
char op = expression.getOperator();
exp.addValue(expression.getValue());
int last_val = -1;
CssBackgroundPosition bg_pos;
bg_pos = new CssBackgroundPosition(ac, exp, check);
// good we have a valid value, try something better..
expression.mark();
// we MUST try all the cases, as we can have something
// invalid using 3 values (incompatible definitions)
// but valid using 4 values...
// example top 12% is invalid, top 12% center is valid...
for (int i = 0; i < 3; i++) {
if ((op == SPACE) && !expression.end()) {
expression.next();
if (expression.end()) {
break;
}
exp.addValue(expression.getValue());
exp.starts();
try {
bg_pos = new CssBackgroundPosition(ac, exp, check);
last_val = i;
} catch (InvalidParamException ipe) {
}
}
}
expression.reset();
while (last_val >= 0) {
expression.next();
last_val--;
}
return bg_pos.get();
}
public CssBackgroundValue check(ApplContext ac, CssExpression expression,
boolean check, boolean is_final)
throws InvalidParamException {
// = || || / || ||
// ||
// bg_image is CSS_URL | IDENT
// bg-position is IDENT | NUMBER | LENGTH | PERCENTAGE
// bg-size is IDENT | NUMBER | LENGTH | PERCENTAGE
// repeat-style is IDENT
// attachment is IDENT
// bg-origin is IDENT
// + color as CSS_COLOR or IDENT on final-layer
CssValue val;
char op;
CssExpression exp;
CssBackgroundValue v = new CssBackgroundValue();
boolean next_is_size, got_size, prev_is_position;
Object res;
next_is_size = false;
got_size = false;
prev_is_position = false;
while (!expression.end()) {
val = expression.getValue();
op = expression.getOperator();
switch (val.getType()) {
case CssTypes.CSS_HASH_IDENT:
case CssTypes.CSS_COLOR:
prev_is_position = false;
// we already got one, fail...
if (v.color != null || next_is_size || !is_final) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
exp = new CssExpression();
exp.addValue(val);
CssBackgroundColor bg_color;
bg_color = new CssBackgroundColor(ac, exp, check);
v.color = (CssValue) bg_color.get();
break;
case CssTypes.CSS_URL:
case CssTypes.CSS_IMAGE:
prev_is_position = false;
// we already got one, fail...
if (v.bg_image != null || next_is_size) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
exp = new CssExpression();
exp.addValue(val);
CssBackgroundImage bg_image;
bg_image = new CssBackgroundImage(ac, exp, check);
res = bg_image.get();
// we only have one vale so it should always be the case
if (res instanceof CssValue) {
v.bg_image = (CssValue) res;
} else {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
break;
case CssTypes.CSS_NUMBER:
case CssTypes.CSS_LENGTH:
case CssTypes.CSS_PERCENTAGE:
prev_is_position = false;
// ok, so now we have a background position or size.
// and...
// in : where '' must occur before
// '/ ' if both are present.
if (next_is_size) {
// size, we have up to two values
if (v.bg_size != null) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
res = getCssBackgroundSizeValue(ac, expression, check);
op = expression.getOperator();
// we only have one vale so it should always be the case
if (res instanceof CssValue) {
v.bg_size = (CssValue) res;
} else {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
got_size = true;
next_is_size = false;
} else {
// position with it's up to 4 values...
if (got_size) {
throw new InvalidParamException("bg_order", val,
getPropertyName(), ac);
}
if (v.bg_position != null) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
res = getCssBackgroundPositionValue(ac, expression, check);
op = expression.getOperator();
prev_is_position = true;
// we only have one value so it should always be the case
if (res instanceof CssValue) {
v.bg_position = (CssValue) res;
} else {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
}
break;
case CssTypes.CSS_IDENT:
prev_is_position = false;
// inherit is already taken care of...
CssIdent ident_val = (CssIdent) val;
if (CssBackgroundAttachment.isMatchingIdent(ident_val)) {
if (v.attachment != null || next_is_size) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
exp = new CssExpression();
exp.addValue(val);
CssBackgroundAttachment attachment;
attachment = new CssBackgroundAttachment(ac, exp, check);
res = attachment.get();
// we only have one vale so it should always be the case
if (res instanceof CssValue) {
v.attachment = (CssValue) res;
} else {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
break;
}
if (CssBackgroundImage.isMatchingIdent(ident_val)) {
if (v.bg_image != null || next_is_size) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
// a bit of an overkill, as we know it can be only
// 'none'.. but it is more flexible if ever it changes
exp = new CssExpression();
exp.addValue(val);
bg_image = new CssBackgroundImage(ac, exp, check);
res = bg_image.get();
// we only have one vale so it should always be the case
if (res instanceof CssValue) {
v.bg_image = (CssValue) res;
} else {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
break;
}
// Kludge ahead, we are testing for here, and it
// matches both origin and clip.
if (CssBackgroundOrigin.isMatchingIdent(ident_val)) {
if (next_is_size) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
if (v.origin != null) {
// ok, we have an origin, so we are looking for a clip
if (v.clip != null) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
exp = new CssExpression();
exp.addValue(val);
CssBackgroundClip clip;
clip = new CssBackgroundClip(ac, exp, check);
res = clip.get();
// we only have one vale so it should always be the case
if (res instanceof CssValue) {
v.clip = (CssValue) res;
} else {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
break;
}
exp = new CssExpression();
exp.addValue(val);
CssBackgroundOrigin origin;
origin = new CssBackgroundOrigin(ac, exp, check);
res = origin.get();
// we only have one vale so it should always be the case
if (res instanceof CssValue) {
v.origin = (CssValue) res;
} else {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
break;
}
if (CssBackgroundRepeat.isMatchingIdent(ident_val)) {
if (v.repeat_style != null || next_is_size) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
res = getCssBackgroundRepeatValue(ac, expression, check);
op = expression.getOperator();
// we only have one vale so it should always be the case
if (res instanceof CssValue) {
v.repeat_style = (CssValue) res;
} else {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
break;
}
if (next_is_size) {
if (CssBackgroundSize.isMatchingIdent(ident_val)) {
// size, we have up to two values
if (v.bg_size != null) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
res = getCssBackgroundSizeValue(ac, expression, check);
op = expression.getOperator();
// we only have one vale so it should always be the case
if (res instanceof CssValue) {
v.bg_size = (CssValue) res;
} else {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
got_size = true;
next_is_size = false;
break;
}
} else {
if (CssBackgroundPosition.isMatchingIdent(ident_val)) {
// position with it's up to 4 values...
if (got_size) {
throw new InvalidParamException("bg_order",
val, getPropertyName(), ac);
}
if (v.bg_position != null) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
res = getCssBackgroundPositionValue(ac, expression, check);
op = expression.getOperator();
prev_is_position = true;
// we only have one vale so it should always be the case
if (res instanceof CssValue) {
v.bg_position = (CssValue) res;
} else {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
break;
}
}
// last one remaining... value!
// or else, it will fail :)
if (is_final) {
if (v.color != null || next_is_size) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
exp = new CssExpression();
exp.addValue(val);
bg_color = new CssBackgroundColor(ac, exp, check);
v.color = (CssValue) bg_color.get();
break;
}
// unrecognized or unwanted ident
// let it fail now
case CssTypes.CSS_FUNCTION:
prev_is_position = false;
// function can only be a value here
// we already got one, fail...
if (v.color != null || next_is_size || !is_final) {
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
exp = new CssExpression();
exp.addValue(val);
bg_color = new CssBackgroundColor(ac, exp, check);
v.color = (CssValue) bg_color.get();
break;
// the infamous switch...
// note that we should check that we got something first.
case CssTypes.CSS_SWITCH:
if (!prev_is_position) {
throw new InvalidParamException("operator", val,
getPropertyName(), ac);
}
next_is_size = true;
break;
default:
throw new InvalidParamException("value", val,
getPropertyName(), ac);
}
if (op != SPACE) {
throw new InvalidParamException("operator", op,
getPropertyName(), ac);
}
expression.next();
}
align_bg_values(v);
return v;
}
private void align_bg_values(CssBackgroundValue v) {
// = || || / || ||
// ||
Object value;
if (v.bg_image == null) {
value = (new CssBackgroundImage()).get();
if (value instanceof CssValue) {
v.bg_image_value = (CssValue) value;
}
} else {
v.bg_image_value = v.bg_image;
}
if (v.bg_position == null) {
value = (new CssBackgroundPosition()).get();
if (value instanceof CssValue) {
v.bg_position_value = (CssValue) value;
}
} else {
v.bg_position_value = v.bg_position;
}
if (v.bg_size == null) {
value = (new CssBackgroundSize()).get();
if (value instanceof CssValue) {
v.bg_size_value = (CssValue) value;
}
} else {
v.bg_size_value = v.bg_size;
}
if (v.repeat_style == null) {
value = (new CssBackgroundRepeat()).get();
if (value instanceof CssValue) {
v.repeat_style_value = (CssValue) value;
}
} else {
v.repeat_style_value = v.repeat_style;
}
if (v.attachment == null) {
value = (new CssBackgroundAttachment()).get();
if (value instanceof CssValue) {
v.attachment_value = (CssValue) value;
}
} else {
v.attachment_value = v.attachment;
}
if (v.origin == null) {
value = (new CssBackgroundOrigin()).get();
if (value instanceof CssValue) {
CssValue css_val = (CssValue) value;
v.origin_value = (CssValue) value;
// If 'background-origin' is present and its value matches a
// possible value for 'background-clip' then it also sets
// 'background-clip' to that value.
if (v.clip == null && (css_val.getType() == CssTypes.CSS_IDENT) &&
CssBackgroundClip.isMatchingIdent((CssIdent) css_val)) {
v.clip_value = v.origin_value;
}
}
} else {
v.origin_value = v.origin;
}
if (v.clip != null) {
v.clip_value = v.clip;
}
if (v.color == null) {
v.color_value = (new CssBackgroundColor()).getColor();
} else {
v.color_value = v.color;
}
}
/**
* Transform the compound value into the equivalent individual
* values (used for conflict check, like color and background-color
* Note that the value verification already took place, so no need
* for extra check
*/
private void transform_into_individual_values() {
if (value instanceof CssBackgroundValue) {
CssBackgroundValue v = (CssBackgroundValue) value;
if (v.color != null) {
color = new CssBackgroundColor();
color.set(v.color_value);
}
if (v.bg_image != null) {
image = new CssBackgroundImage();
image.value = v.bg_image_value;
}
if (v.repeat_style != null) {
repeat = new CssBackgroundRepeat();
repeat.value = v.repeat_style_value;
}
if (v.attachment != null) {
attachment = new CssBackgroundAttachment();
attachment.value = v.attachment_value;
}
if (v.bg_position != null) {
position = new CssBackgroundPosition();
position.value = v.bg_position_value;
}
if (v.bg_size != null) {
size = new CssBackgroundSize();
size.value = v.bg_size_value;
}
} else if (value instanceof CssLayerList) {
ArrayList vlist = (ArrayList) value.get();
int len = vlist.size();
ArrayList images = new ArrayList(len);
ArrayList repeats = new ArrayList(len);
ArrayList positions = new ArrayList(len);
ArrayList attachments = new ArrayList(len);
ArrayList sizes = new ArrayList(len);
for (int i = 0; i < len; i++) {
CssBackgroundValue v = (CssBackgroundValue) vlist.get(i);
images.add(v.bg_image_value);
repeats.add(v.repeat_style_value);
positions.add(v.bg_position_value);
attachments.add(v.attachment_value);
sizes.add(v.bg_size_value);
if (v.color != null) {
color = new CssBackgroundColor();
color.set(v.color_value);
}
}
image = new CssBackgroundImage();
image.value = new CssLayerList(images);
repeat = new CssBackgroundRepeat();
repeat.value = new CssLayerList(repeats);
attachment = new CssBackgroundAttachment();
attachment.value = new CssLayerList(attachments);
position = new CssBackgroundPosition();
position.value = new CssLayerList(positions);
size = new CssBackgroundSize();
size.value = new CssLayerList(sizes);
} else {
// FIXME TODO use inherit?
image = null;
repeat = null;
attachment = null;
color = null;
size = null;
position = null;
}
}
/**
* Returns the value of this property
*/
public Object get() {
return value;
}
/**
* Returns the color
*/
public CssValue getColor() {
if (color == null) {
return null;
} else {
return color.getColor();
}
}
// placeholder for the different values
public class CssBackgroundValue extends CssValueList {
CssValue bg_image = null;
CssValue bg_position = null;
CssValue bg_size = null;
CssValue repeat_style = null;
CssValue attachment = null;
CssValue origin = null;
CssValue clip = null;
CssValue color = null;
CssValue bg_image_value = null;
CssValue bg_position_value = null;
CssValue bg_size_value = null;
CssValue repeat_style_value = null;
CssValue attachment_value = null;
CssValue origin_value = null;
// If 'background-origin' is present and its value matches a possible
// value for 'background-clip' then it also sets 'background-clip' to
// that value.
CssValue clip_value = null;
CssValue color_value = null;
public boolean equals(CssBackgroundValue v) {
if (bg_image_value == null) {
if (v.bg_image_value != null) {
return false;
}
} else if (!bg_image_value.equals(v.bg_image_value)) {
return false;
}
if (bg_position_value == null) {
if (v.bg_position_value != null) {
return false;
}
} else if (!bg_position_value.equals(v.bg_position_value)) {
return false;
}
if (bg_size_value == null) {
if (v.bg_size_value != null) {
return false;
}
} else if (!bg_size_value.equals(v.bg_size_value)) {
return false;
}
if (repeat_style_value == null) {
if (v.repeat_style_value != null) {
return false;
}
} else if (!repeat_style_value.equals(v.repeat_style_value)) {
return false;
}
if (attachment_value == null) {
if (v.attachment_value != null) {
return false;
}
} else if (!attachment_value.equals(v.attachment_value)) {
return false;
}
if (origin_value == null) {
if (v.origin_value != null) {
return false;
}
} else if (!origin_value.equals(v.origin_value)) {
return false;
}
if (clip_value == null) {
if (v.clip_value != null) {
return false;
}
} else if (!clip_value.equals(v.clip_value)) {
return false;
}
if (color_value == null) {
if (v.color_value != null) {
return false;
}
} else if (!color_value.equals(v.color_value)) {
return false;
}
// at last!
return true;
}
public String toString() {
StringBuilder sb = new StringBuilder();
if (bg_image != null) {
sb.append(bg_image).append(' ');
}
if (bg_position != null) {
sb.append(bg_position).append(' ');
if (bg_size != null) {
sb.append('/').append(bg_size).append(' ');
}
}
if (repeat_style != null) {
sb.append(repeat_style).append(' ');
}
if (attachment != null) {
sb.append(attachment).append(' ');
}
if (origin != null) {
sb.append(origin).append(' ');
}
if (clip != null) {
sb.append(clip).append(' ');
}
if (color != null) {
sb.append(color);
} else {
int sb_length = sb.length();
if (sb_length > 0) {
sb.setLength(sb_length - 1);
}
}
return sb.toString();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy