w3c.css.properties.css3.CssGridTemplate Maven / Gradle / Ivy
//
// Author: Yves Lafon
//
// (c) COPYRIGHT MIT, ERCIM, Keio, Beihang, 2017.
// Please first read the full copyright statement in file COPYRIGHT.html
package org.w3c.css.properties.css3;
import org.w3c.css.parser.CssStyle;
import org.w3c.css.properties.css.CssProperty;
import org.w3c.css.util.ApplContext;
import org.w3c.css.util.InvalidParamException;
import org.w3c.css.values.CssBracket;
import org.w3c.css.values.CssCheckableValue;
import org.w3c.css.values.CssExpression;
import org.w3c.css.values.CssFunction;
import org.w3c.css.values.CssIdent;
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.properties.css3.CssGridAutoRows.parseFixedSize;
import static org.w3c.css.properties.css3.CssGridAutoRows.parseTrackSize;
import static org.w3c.css.values.CssOperator.COMMA;
import static org.w3c.css.values.CssOperator.SPACE;
/**
* @spec https://www.w3.org/TR/2017/CR-css-grid-1-20170209/#propdef-grid-template
*/
public class CssGridTemplate extends org.w3c.css.properties.css.CssGridTemplate {
public static final CssIdent[] allowed_repeat_values;
public static final String repeat_func = "repeat";
static {
String[] _allowed_repeat_values = {"auto-fill", "auto-fit"};
allowed_repeat_values = new CssIdent[_allowed_repeat_values.length];
int i = 0;
for (String s : _allowed_repeat_values) {
CssGridTemplate.allowed_repeat_values[i++] = CssIdent.getIdent(s);
}
}
private CssGridTemplateAreas cssGridTemplateAreas;
private CssGridTemplateColumns cssGridTemplateColumns;
private CssGridTemplateRows cssGridTemplateRows;
/**
* Create a new CssGridTemplate
*/
public CssGridTemplate() {
value = initial;
cssGridTemplateAreas = new CssGridTemplateAreas();
cssGridTemplateColumns = new CssGridTemplateColumns();
cssGridTemplateRows = new CssGridTemplateRows();
}
/**
* Creates a new CssGridTemplate
*
* @param expression The expression for this property
* @throws org.w3c.css.util.InvalidParamException
* Expressions are incorrect
*/
public CssGridTemplate(ApplContext ac, CssExpression expression, boolean check)
throws InvalidParamException {
setByUser();
cssGridTemplateAreas = new CssGridTemplateAreas();
cssGridTemplateColumns = new CssGridTemplateColumns();
cssGridTemplateRows = new CssGridTemplateRows();
value = parseGridTemplate(ac, expression, this, cssGridTemplateAreas,
cssGridTemplateColumns, cssGridTemplateRows);
}
protected static CssValue parseGridTemplate(ApplContext ac, CssExpression expression,
CssProperty caller,
CssGridTemplateAreas areas,
CssGridTemplateColumns columns,
CssGridTemplateRows rows)
throws InvalidParamException {
ArrayList values = new ArrayList<>();
ArrayList areaValues = new ArrayList<>();
ArrayList columnValues = new ArrayList<>();
ArrayList rowValues = new ArrayList<>();
CssValue val = null;
CssValue v;
char op;
if (expression.getCount() == 1) {
// can only be 'none' or 'inherit'
val = expression.getValue();
if (val.getType() != CssTypes.CSS_IDENT) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
CssIdent id = (CssIdent) val;
if (none.equals(id)) {
values.add(none);
areaValues.add(none);
columnValues.add(none);
rowValues.add(none);
} else if (inherit.equals(id)) {
values.add(inherit);
areaValues.add(inherit);
columnValues.add(inherit);
rowValues.add(inherit);
} else {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
expression.next();
} else {
// check if there is a CssString element to decide which case we are in.
boolean got_string = false;
while (!expression.end() && !got_string) {
val = expression.getValue();
got_string = (val.getType() == CssTypes.CSS_STRING);
expression.next();
}
expression.starts();
if (!got_string) {
// we should have /
CssExpression nex = new CssExpression();
boolean got_slash = false;
while (!got_slash && !expression.end()) {
val = expression.getValue();
op = expression.getOperator();
got_slash = (val.getType() == CssTypes.CSS_SWITCH);
if (got_slash) {
if (op != SPACE) {
throw new InvalidParamException("operator", op,
caller.getPropertyName(), ac);
}
} else {
nex.addValue(val);
nex.setOperator(op);
}
expression.next();
}
if (!got_slash) {
throw new InvalidParamException("unrecognize", ac);
}
v = parseTemplateRows(ac, nex, caller);
rowValues.add(v);
values.add(v);
values.add(val);
nex = new CssExpression();
while (!expression.end()) {
val = expression.getValue();
op = expression.getOperator();
nex.addValue(val);
nex.setOperator(op);
expression.next();
}
v = parseTemplateRows(ac, nex, caller);
columnValues.add(v);
values.add(v);
areaValues.add(none);
} else {
// [ ? ? ? ]+ [ / ]?
boolean got_slash = false;
CssExpression nex = new CssExpression();
boolean in_line_names = false;
int got_line_names = 1; // why 1? because we can have only 1 first
while (!got_slash && !expression.end()) {
val = expression.getValue();
op = expression.getOperator();
switch (val.getType()) {
case CssTypes.CSS_STRING:
if (in_line_names) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
got_line_names = 0;
areaValues.add(val);
values.add(val);
break;
case CssTypes.CSS_BRACKET:
CssBracket bracket = (CssBracket) val;
if (bracket.isLeft()) {
if (in_line_names || (got_line_names > 2)) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
in_line_names = true;
} else { // bracket.isRight() but it can't be anything else...
if (!in_line_names) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
got_line_names++;
in_line_names = false;
}
values.add(val);
rowValues.add(val);
break;
case CssTypes.CSS_SWITCH:
got_slash = true;
values.add(val);
break;
case CssTypes.CSS_IDENT:
if (in_line_names) {
values.add(val);
rowValues.add(val);
break;
}
default:
v = parseTrackSize(ac, val, caller);
values.add(v);
rowValues.add(v);
}
if (op != SPACE) {
throw new InvalidParamException("operator", op,
caller.getPropertyName(), ac);
}
expression.next();
}
if (got_slash) {
while (!expression.end()) {
val = expression.getValue();
op = expression.getOperator();
nex.addValue(val);
nex.setOperator(op);
expression.next();
}
v = parseExplicitTrackList(ac, nex, caller);
columnValues.add(v);
values.add(v);
} else {
columnValues.add(none);
}
}
}
if (areas != null) {
areas.value = (areaValues.size() == 1) ? areaValues.get(0) : new CssValueList(areaValues);
}
if (columns != null) {
columns.value = (columnValues.size() == 1) ? columnValues.get(0) : new CssValueList(columnValues);
}
if (rows != null) {
rows.value = (rowValues.size() == 1) ? rowValues.get(0) : new CssValueList(rowValues);
}
return (values.size() == 1) ? values.get(0) : new CssValueList(values);
}
public CssGridTemplate(ApplContext ac, CssExpression expression)
throws InvalidParamException {
this(ac, expression, false);
}
public static CssIdent getAllowedRepeatIdent(CssIdent ident) {
for (CssIdent id : allowed_repeat_values) {
if (id.equals(ident)) {
return id;
}
}
return null;
}
protected static CssValue parseTemplateRows(ApplContext ac, CssExpression exp, CssProperty caller)
throws InvalidParamException {
if (exp.getCount() == 1) {
CssValue val = exp.getValue();
if (val.getType() == CssTypes.CSS_IDENT && none.equals((CssIdent) val)) {
exp.next();
return none;
}
}
exp.mark();
try {
return parseTrackList(ac, exp, caller);
} catch (InvalidParamException ex) {
// perhaps an AutoTrackList?
exp.reset();
return parseAutoTrackList(ac, exp, caller);
}
}
protected static CssValue parseTrackList(ApplContext ac, CssExpression exp, CssProperty caller)
throws InvalidParamException {
ArrayList values = new ArrayList<>();
CssValue val;
char op;
boolean in_line_names = false;
boolean got_line_names = false;
boolean got_size = false;
while (!exp.end()) {
val = exp.getValue();
op = exp.getOperator();
switch (val.getType()) {
case CssTypes.CSS_BRACKET:
CssBracket bracket = (CssBracket) val;
if (bracket.isLeft()) {
if (in_line_names || got_line_names) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
in_line_names = true;
} else { // bracket.isRight() but it can't be anything else...
if (!in_line_names) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
got_line_names = true;
in_line_names = false;
}
values.add(val);
break;
case CssTypes.CSS_IDENT:
if (in_line_names) {
// todo check unreserved words
values.add(val);
break;
}
values.add(parseTrackSize(ac, val, caller));
got_line_names = false;
got_size = true;
break;
case CssTypes.CSS_FUNCTION:
CssFunction function = (CssFunction) val;
if (repeat_func.equals(function.getName())) {
values.add(parseRepeatFunction(ac, function, RepeatType.TRACK_REPEAT, caller));
got_line_names = false;
got_size = true;
break;
}
// not a repeat function, let it flow.
default:
// should be a tracksize, or fail.
values.add(parseTrackSize(ac, val, caller));
got_size = true;
got_line_names = false;
}
if (op != SPACE) {
throw new InvalidParamException("operator", op,
caller.getPropertyName(), ac);
}
exp.next();
}
if (values.isEmpty() || !got_size) {
throw new InvalidParamException("unrecognize", ac);
}
return (values.size() == 1) ? values.get(0) : new CssValueList(values);
}
protected static CssValue parseExplicitTrackList(ApplContext ac, CssExpression exp,
CssProperty caller)
throws InvalidParamException {
ArrayList values = new ArrayList<>();
CssValue val;
char op;
boolean in_line_names = false;
boolean got_line_names = false;
boolean got_size = false;
while (!exp.end()) {
val = exp.getValue();
op = exp.getOperator();
switch (val.getType()) {
case CssTypes.CSS_BRACKET:
CssBracket bracket = (CssBracket) val;
if (bracket.isLeft()) {
if (in_line_names || got_line_names) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
in_line_names = true;
} else { // bracket.isRight() but it can't be anything else...
if (!in_line_names) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
got_line_names = true;
in_line_names = false;
}
values.add(val);
break;
case CssTypes.CSS_IDENT:
if (in_line_names) {
// todo check unreserved words
values.add(val);
break;
}
default:
// should be a tracksize, or fail.
values.add(parseTrackSize(ac, val, caller));
got_size = true;
got_line_names = false;
}
if (op != SPACE) {
throw new InvalidParamException("operator", op,
caller.getPropertyName(), ac);
}
exp.next();
}
if (values.isEmpty() || !got_size) {
throw new InvalidParamException("unrecognize", ac);
}
return (values.size() == 1) ? values.get(0) : new CssValueList(values);
}
protected static CssValue parseAutoTrackList(ApplContext ac, CssExpression exp, CssProperty caller)
throws InvalidParamException {
ArrayList values = new ArrayList<>();
CssValue val;
char op;
boolean in_line_names = false;
boolean got_line_names = false;
boolean got_auto = false;
while (!exp.end()) {
val = exp.getValue();
op = exp.getOperator();
switch (val.getType()) {
case CssTypes.CSS_BRACKET:
CssBracket bracket = (CssBracket) val;
if (bracket.isLeft()) {
if (in_line_names || got_line_names) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
in_line_names = true;
} else { // bracket.isRight() but it can't be anything else...
if (!in_line_names) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
got_line_names = true;
in_line_names = false;
}
values.add(val);
break;
case CssTypes.CSS_IDENT:
if (in_line_names) {
// todo check unreserved words
values.add(val);
break;
}
// no other ident allowed.
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
case CssTypes.CSS_FUNCTION:
CssFunction function = (CssFunction) val;
if (repeat_func.equals(function.getName())) {
if (exp.getRemainingCount() == 1) {
values.add(parseRepeatFunction(ac, function, RepeatType.AUTO_REPEAT, caller));
got_auto = true;
} else {
values.add(parseRepeatFunction(ac, function, RepeatType.FIXED_REPEAT, caller));
}
got_line_names = false;
break;
}
// not a repeat function, let it flow.
default:
// should be a tracksize, or fail.
values.add(parseTrackSize(ac, val, caller));
got_line_names = false;
}
if (op != SPACE) {
throw new InvalidParamException("operator", op,
caller.getPropertyName(), ac);
}
exp.next();
}
if (values.isEmpty() || !got_auto) {
throw new InvalidParamException("unrecognize", ac);
}
return (values.size() == 1) ? values.get(0) : new CssValueList(values);
}
/**
* @spec https://www.w3.org/TR/2017/CR-css-grid-1-20170209/#funcdef-repeat
*/
protected static CssFunction parseRepeatFunction(ApplContext ac, CssFunction func,
RepeatType type,
CssProperty caller)
throws InvalidParamException {
CssExpression exp = func.getParameters();
CssExpression nex;
CssValue val;
char op;
if (exp.getCount() < 2) {
throw new InvalidParamException("unrecognize", ac);
}
nex = new CssExpression();
val = exp.getValue();
op = exp.getOperator();
switch (val.getType()) {
case CssTypes.CSS_IDENT:
CssIdent id = getAllowedRepeatIdent((CssIdent) val);
if (id == null || type != RepeatType.AUTO_REPEAT) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
break;
case CssTypes.CSS_NUMBER:
if (type != RepeatType.TRACK_REPEAT && type != RepeatType.FIXED_REPEAT) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
CssCheckableValue v = val.getCheckableValue();
v.checkInteger(ac, caller);
v.checkPositiveness(ac, caller);
break;
default:
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
if (op != COMMA) {
throw new InvalidParamException("operator", op,
caller.getPropertyName(), ac);
}
exp.next();
boolean got_line_names = false;
boolean in_line_names = false;
while (!exp.end()) {
val = exp.getValue();
op = exp.getOperator();
switch (val.getType()) {
case CssTypes.CSS_BRACKET:
CssBracket bracket = (CssBracket) val;
if (bracket.isLeft()) {
if (in_line_names || got_line_names) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
in_line_names = true;
} else { // bracket.isRight() but it can't be anything else...
if (!in_line_names) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
got_line_names = true;
in_line_names = false;
}
break;
case CssTypes.CSS_IDENT:
if (in_line_names) {
// todo check unreserved words
break;
}
// same branch for FLEX as it can only be TRACK_REPEAT.
case CssTypes.CSS_FLEX:
if (type == RepeatType.TRACK_REPEAT) {
parseTrackSize(ac, val, caller);
got_line_names = false;
}
break;
case CssTypes.CSS_NUMBER:
case CssTypes.CSS_LENGTH:
case CssTypes.CSS_PERCENTAGE:
case CssTypes.CSS_FUNCTION:
switch (type) {
case AUTO_REPEAT:
case FIXED_REPEAT:
parseFixedSize(ac, val, caller);
break;
case TRACK_REPEAT:
parseTrackSize(ac, val, caller);
break;
default:
// wrong type?
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
// we made it! now wait for a possible line name
got_line_names = false;
break;
default:
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
if (op != SPACE) {
throw new InvalidParamException("operator", op,
caller.getPropertyName(), ac);
}
exp.next();
}
exp.starts();
// we reached the end without closing the line-names...
if (in_line_names) {
throw new InvalidParamException("value",
val.toString(),
caller.getPropertyName(), ac);
}
return func;
}
/**
* Add this property to the CssStyle.
*
* @param style The CssStyle
*/
public void addToStyle(ApplContext ac, CssStyle style) {
super.addToStyle(ac, style);
cssGridTemplateAreas.addToStyle(ac, style);
cssGridTemplateColumns.addToStyle(ac, style);
cssGridTemplateRows.addToStyle(ac, style);
}
protected enum RepeatType {TRACK_REPEAT, AUTO_REPEAT, FIXED_REPEAT}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy