001: /*
002: ItsNat Java Web Application Framework
003: Copyright (C) 2007 Innowhere Software Services S.L., Spanish Company
004: Author: Jose Maria Arranz Santamaria
005:
006: This program is free software: you can redistribute it and/or modify
007: it under the terms of the GNU Affero General Public License as published by
008: the Free Software Foundation, either version 3 of the License, or
009: (at your option) any later version. See the GNU Affero General Public
010: License for more details. See the copy of the GNU Affero General Public License
011: included in this program. If not, see <http://www.gnu.org/licenses/>.
012: */
013:
014: package org.itsnat.impl.core.css;
015:
016: import org.itsnat.core.ItsNatException;
017: import org.w3c.dom.DOMException;
018: import org.w3c.dom.css.CSSPrimitiveValue;
019: import org.w3c.dom.css.Counter;
020: import org.w3c.dom.css.RGBColor;
021: import org.w3c.dom.css.Rect;
022: import org.itsnat.impl.core.css.lex.FloatNumber;
023: import org.itsnat.impl.core.css.lex.HexNumber;
024: import org.itsnat.impl.core.css.lex.Identifier;
025: import org.itsnat.impl.core.css.lex.ParenthesisBlock;
026: import org.itsnat.impl.core.css.lex.SourceCode;
027: import org.itsnat.impl.core.css.lex.StringDoubleQuote;
028: import org.itsnat.impl.core.css.lex.StringSimpleQuote;
029: import org.itsnat.impl.core.css.lex.Token;
030:
031: /**
032: * http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue
033: * http://www.w3.org/TR/REC-CSS2/grammar.html
034: * http://www.w3.org/TR/REC-CSS2/syndata.html
035: * http://www.princexml.com/doc/properties/
036: *
037: * @author jmarranz
038: */
039: public class CSSPrimitiveValueImpl extends CSSValueImpl implements
040: CSSPrimitiveValue {
041: protected short primitiveType = -1;
042: protected float floatValue;
043: protected Counter counterValue;
044: protected Rect rectValue;
045: protected RGBColor rgbValue;
046:
047: /** Creates a new instance of CSSPrimitiveValueImpl */
048: public CSSPrimitiveValueImpl(SourceCode cssTextCode, int code,
049: ObjectValueParent parent) {
050: super (cssTextCode, code, parent);
051: rebuild(cssTextCode);
052: }
053:
054: public static String floatUnitToString(short unitType) {
055: switch (unitType) {
056: case CSS_PERCENTAGE:
057: return "%";
058: case CSS_S:
059: return "s";
060: case CSS_EMS:
061: return "em";
062: case CSS_EXS:
063: return "ex";
064: case CSS_PX:
065: return "px";
066: case CSS_CM:
067: return "cm";
068: case CSS_MM:
069: return "mm";
070: case CSS_IN:
071: return "in";
072: case CSS_PT:
073: return "pt";
074: case CSS_PC:
075: return "pc";
076: case CSS_MS:
077: return "ms";
078: case CSS_HZ:
079: return "hz";
080: case CSS_DEG:
081: return "deg";
082: case CSS_RAD:
083: return "rad";
084: case CSS_KHZ:
085: return "khz";
086: case CSS_GRAD:
087: return "grad";
088: case CSS_NUMBER:
089: return "";
090: case CSS_DIMENSION:
091: return ""; // Precisamente por ser desconocida no podemos saber su nombre
092: }
093:
094: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
095: "Invalid unit type: " + unitType);
096: }
097:
098: public boolean setFloatValueInternal(SourceCode cssTextCode) {
099: // Se supone que hay almenos un token
100: Token token0 = cssTextCode.getToken(0);
101: if (!(token0 instanceof FloatNumber))
102: return false;
103:
104: FloatNumber tokenNumber = (FloatNumber) token0;
105: float floatValue = tokenNumber.getFloat();
106: short unitType = -1;
107:
108: if (cssTextCode.tokenCount() > 2)
109: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
110: "CSS: number format error: "
111: + cssTextCode.toString());
112:
113: if (cssTextCode.tokenCount() == 1) {
114: // No tiene sufijo
115: unitType = CSS_NUMBER;
116: } else {
117: // Tiene sufijo
118: Token token1 = cssTextCode.getToken(1);
119: if (!(token1 instanceof Identifier))
120: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
121: "CSS: expected a unit identifier: "
122: + cssTextCode.toString());
123: Identifier tokenUnit = (Identifier) token1;
124:
125: String suffix = tokenUnit.toString();
126: suffix = suffix.toLowerCase();
127:
128: if ("%".equals(suffix)) {
129: unitType = CSS_PERCENTAGE;
130: } else if ("s".equals(suffix)) {
131: unitType = CSS_PERCENTAGE;
132: } else if ("em".equals(suffix)) {
133: unitType = CSS_EMS;
134: } else if ("ex".equals(suffix)) {
135: unitType = CSS_EXS;
136: } else if ("px".equals(suffix)) {
137: unitType = CSS_PX;
138: } else if ("cm".equals(suffix)) {
139: unitType = CSS_CM;
140: } else if ("mm".equals(suffix)) {
141: unitType = CSS_MM;
142: } else if ("in".equals(suffix)) {
143: unitType = CSS_IN;
144: } else if ("pt".equals(suffix)) {
145: unitType = CSS_PT;
146: } else if ("pc".equals(suffix)) {
147: unitType = CSS_PC;
148: } else if ("ms".equals(suffix)) {
149: unitType = CSS_MS;
150: } else if ("hz".equals(suffix)) {
151: unitType = CSS_HZ;
152: } else if ("deg".equals(suffix)) {
153: unitType = CSS_DEG;
154: } else if ("rad".equals(suffix)) {
155: unitType = CSS_RAD;
156: } else if ("khz".equals(suffix)) {
157: unitType = CSS_KHZ;
158: } else if ("grad".equals(suffix)) {
159: unitType = CSS_GRAD;
160: } else // Unidad desconocida, es el caso CSS_DIMENSION
161: {
162: unitType = CSS_DIMENSION;
163: }
164: }
165:
166: this .primitiveType = unitType;
167: this .floatValue = floatValue;
168:
169: return true;
170: }
171:
172: public boolean setFunctionBasedValueInternal(SourceCode cssTextCode) {
173: if (cssTextCode.tokenCount() != 2)
174: return false;
175:
176: Token token0 = cssTextCode.getToken(0);
177: if (!(token0 instanceof Identifier))
178: return false;
179: Identifier tokIdent = (Identifier) token0;
180:
181: Token token1 = cssTextCode.getToken(1);
182: if (!(token1 instanceof ParenthesisBlock))
183: return false;
184: ParenthesisBlock tokBlock = (ParenthesisBlock) token1;
185:
186: String identif = tokIdent.toString();
187: identif = identif.toLowerCase();
188:
189: if (identif.equals("url")) {
190: this .primitiveType = CSS_URI;
191: // No hay ningún objeto especial, el url queda almacenado en this.cssTextCode
192: return true;
193: } else if (identif.equals("rect")) {
194: this .primitiveType = CSS_RECT;
195: this .rectValue = new RectImpl(tokBlock.getContent(), this );
196: return true;
197: } else if (identif.equals("rgb")) {
198: // FALTA procesar los valores #rgb y #rrggbb
199: this .primitiveType = CSS_RGBCOLOR;
200: this .rgbValue = new RGBColorImpl(tokBlock.getContent(),
201: this );
202: return true;
203: } else if (identif.equals("attr")) {
204: this .primitiveType = CSS_ATTR;
205: // No hay ningún objeto especial, el attr queda almacenado en this.cssTextCode
206: return true;
207: } else if (identif.equals("counter")) {
208: // Es un CSS_COUNTER pero no lo soportamos
209: this .primitiveType = CSS_UNKNOWN;
210: // el valor queda almacenado en this.cssTextCode
211: return true;
212: } else if (identif.equals("counters")) {
213: // Es un CSS_COUNTER pero no lo soportamos
214: this .primitiveType = CSS_UNKNOWN;
215: // el valor queda almacenado en this.cssTextCode
216: return true;
217: }
218:
219: return false;
220: }
221:
222: public void rebuild(SourceCode cssTextCode) {
223: cssTextCode = cssTextCode.trim();
224:
225: if (cssTextCode.tokenCount() == 0)
226: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
227: "CSS value is not specified, property: "
228: + parent.getPropertyName() + " value: "
229: + this .cssTextCode.toString());
230:
231: if (setFloatValueInternal(cssTextCode))
232: return;
233:
234: // No es un número ni un número + sufijo
235: if (setFunctionBasedValueInternal(cssTextCode))
236: return;
237:
238: if (cssTextCode.tokenCount() == 1) {
239: Token token = cssTextCode.getToken(0);
240: if ((token instanceof StringDoubleQuote)
241: || (token instanceof StringSimpleQuote)) {
242: this .primitiveType = CSS_STRING;
243: return;
244: } else if (token instanceof Identifier) {
245: this .primitiveType = CSS_IDENT;
246: return;
247: } else if (token instanceof HexNumber) {
248: // Casos #rgb y #rrggbb
249: this .primitiveType = CSS_RGBCOLOR;
250: this .rgbValue = new RGBColorImpl((HexNumber) token,
251: this );
252: return;
253: } else {
254: this .primitiveType = CSS_UNKNOWN;
255: return;
256: }
257: } else {
258: this .primitiveType = CSS_UNKNOWN;
259: return;
260: }
261: }
262:
263: public short getCssValueType() {
264: return CSS_PRIMITIVE_VALUE;
265: }
266:
267: public short getPrimitiveType() {
268: return primitiveType;
269: }
270:
271: public float getFloatValue(short unitType) throws DOMException {
272: if (this .primitiveType != unitType)
273: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
274: "Unit conversions is not supported");
275:
276: getCssText(); // Verifica que no ha cambiado indirectamente
277:
278: return floatValue;
279: }
280:
281: public void setFloatValue(short unitType, float floatValue)
282: throws DOMException {
283: String suffix = floatUnitToString(unitType);
284: String cssText = Float.toString(floatValue) + suffix;
285: setCssTextSourceCode(new SourceCode(cssText), false);
286:
287: this .primitiveType = unitType;
288: this .floatValue = floatValue;
289: }
290:
291: public static boolean isStringBasedType(short primitiveType) {
292: return ((primitiveType == CSS_STRING)
293: || (primitiveType == CSS_URI)
294: || (primitiveType == CSS_IDENT) || (primitiveType == CSS_ATTR));
295: }
296:
297: public String getStringValue() throws DOMException {
298: if (!isStringBasedType(this .primitiveType))
299: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
300: "CSS value doesn't contain a string");
301:
302: String str = getCssText();
303: // Por si la cadena ha sido definida via setCssTextSourceCode
304: str = str.trim();
305: if (primitiveType == CSS_STRING) {
306: // Quitamos las comillas " o ' (es obligatorio que estén)
307: str = str.substring(1, str.length() - 1);
308: }
309:
310: return str;
311: }
312:
313: public void setStringValue(short stringType, String stringValue)
314: throws DOMException {
315: if (!isStringBasedType(this .primitiveType))
316: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
317: "CSS value doesn't contain a string");
318: if (!isStringBasedType(stringType))
319: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
320: "New CSS value doesn't contain a string, invalid string type");
321:
322: stringValue = stringValue.trim();
323: if (primitiveType == CSS_STRING) {
324: // Añadimos los literales (es obligatorio que estén)
325: stringValue = '"' + stringValue + '"';
326: }
327:
328: setCssTextSourceCode(new SourceCode(stringValue), false);
329: this .primitiveType = stringType;
330: }
331:
332: public Counter getCounterValue() throws DOMException {
333: if (primitiveType != CSS_COUNTER)
334: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
335: "CSS value doesn't contain a Counter value");
336:
337: return this .counterValue; // Por ahora devuelve null, no soportamos counters
338: }
339:
340: public Rect getRectValue() throws DOMException {
341: if (primitiveType != CSS_RECT)
342: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
343: "CSS value doesn't contain a Rect value");
344:
345: return rectValue;
346: }
347:
348: public RGBColor getRGBColorValue() throws DOMException {
349: if (primitiveType != CSS_RGBCOLOR)
350: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
351: "CSS value doesn't contain a RGBColor value");
352:
353: return this .rgbValue;
354: }
355:
356: public Object getUpdatedChildObjectValueFromElement(
357: Object requester, int requesterCode) {
358: CSSPrimitiveValueImpl current = (CSSPrimitiveValueImpl) parent
359: .getUpdatedChildObjectValueFromElement(this , getCode());
360: if (current != this ) // Ha cambiado en el elemento (si además hubiera cambiado el tipo de objeto daría error antes)
361: {
362: if (requesterCode == CSSPrimitiveValueLiteralImpl.COUNTER)
363: return current.getCounterValue();
364: else if (requesterCode == CSSPrimitiveValueLiteralImpl.RECT)
365: return current.getRectValue();
366: else if (requesterCode == CSSPrimitiveValueLiteralImpl.RGB)
367: return current.getRGBColorValue();
368:
369: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
370: "INTERNAL ERROR");
371: } else
372: return requester;
373: }
374:
375: public void notifyToElementChangedCSSText(SourceCode cssText,
376: Object requester) {
377: setCssTextSourceCode(cssText, false);
378: }
379: }
|