/* * Copyright 2013-2016 Skynav, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY SKYNAV, INC. AND ITS CONTRIBUTORS “AS IS” AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL SKYNAV, INC. OR ITS CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.skynav.ttv.verifier.ttml; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import org.w3c.dom.Attr; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.Locator; import com.skynav.ttv.model.Model; import com.skynav.ttv.model.ttml1.tt.Body; import com.skynav.ttv.model.ttml1.tt.Break; import com.skynav.ttv.model.ttml1.tt.Division; import com.skynav.ttv.model.ttml1.tt.Paragraph; import com.skynav.ttv.model.ttml1.tt.Region; import com.skynav.ttv.model.ttml1.tt.Set; import com.skynav.ttv.model.ttml1.tt.Span; import com.skynav.ttv.model.ttml1.tt.Style; import com.skynav.ttv.model.ttml1.tt.TimedText; import com.skynav.ttv.model.ttml1.ttd.Direction; import com.skynav.ttv.model.ttml1.ttd.Display; import com.skynav.ttv.model.ttml1.ttd.DisplayAlign; import com.skynav.ttv.model.ttml1.ttd.FontStyle; import com.skynav.ttv.model.ttml1.ttd.FontWeight; import com.skynav.ttv.model.ttml1.ttd.Overflow; import com.skynav.ttv.model.ttml1.ttd.ShowBackground; import com.skynav.ttv.model.ttml1.ttd.TextAlign; import com.skynav.ttv.model.ttml1.ttd.TextDecoration; import com.skynav.ttv.model.ttml1.ttd.UnicodeBidi; import com.skynav.ttv.model.ttml1.ttd.Visibility; import com.skynav.ttv.model.ttml1.ttd.WrapOption; import com.skynav.ttv.model.ttml1.ttd.WritingMode; import com.skynav.ttv.model.value.Length; import com.skynav.ttv.util.Enums; import com.skynav.ttv.util.Location; import com.skynav.ttv.util.Reporter; import com.skynav.ttv.verifier.AbstractVerifier; import com.skynav.ttv.verifier.StyleValueVerifier; import com.skynav.ttv.verifier.StyleVerifier; import com.skynav.ttv.verifier.VerifierContext; import com.skynav.ttv.verifier.ttml.style.BackgroundColorVerifier; import com.skynav.ttv.verifier.ttml.style.ColorVerifier; import com.skynav.ttv.verifier.ttml.style.DirectionVerifier; import com.skynav.ttv.verifier.ttml.style.DisplayAlignVerifier; import com.skynav.ttv.verifier.ttml.style.DisplayVerifier; import com.skynav.ttv.verifier.ttml.style.ExtentVerifier; import com.skynav.ttv.verifier.ttml.style.FontFamilyVerifier; import com.skynav.ttv.verifier.ttml.style.FontSizeVerifier; import com.skynav.ttv.verifier.ttml.style.FontStyleVerifier; import com.skynav.ttv.verifier.ttml.style.FontWeightVerifier; import com.skynav.ttv.verifier.ttml.style.LineHeightVerifier; import com.skynav.ttv.verifier.ttml.style.OpacityVerifier; import com.skynav.ttv.verifier.ttml.style.OriginVerifier; import com.skynav.ttv.verifier.ttml.style.OverflowVerifier; import com.skynav.ttv.verifier.ttml.style.PaddingVerifier; import com.skynav.ttv.verifier.ttml.style.RegionAttributeVerifier; import com.skynav.ttv.verifier.ttml.style.ShowBackgroundVerifier; import com.skynav.ttv.verifier.ttml.style.StyleAttributeVerifier; import com.skynav.ttv.verifier.ttml.style.TextAlignVerifier; import com.skynav.ttv.verifier.ttml.style.TextDecorationVerifier; import com.skynav.ttv.verifier.ttml.style.TextOutlineVerifier; import com.skynav.ttv.verifier.ttml.style.UnicodeBidiVerifier; import com.skynav.ttv.verifier.ttml.style.VisibilityVerifier; import com.skynav.ttv.verifier.ttml.style.WrapOptionVerifier; import com.skynav.ttv.verifier.ttml.style.WritingModeVerifier; import com.skynav.ttv.verifier.ttml.style.ZIndexVerifier; import com.skynav.ttv.verifier.util.IdReferences; import com.skynav.ttv.verifier.util.Strings; import static com.skynav.ttv.model.ttml.TTML1.Constants.*; public class TTML1StyleVerifier extends AbstractVerifier implements StyleVerifier { public static final String NAMESPACE = NAMESPACE_TT_STYLE; public static final int APPLIES_TO_TT = 0x00000001; public static final int APPLIES_TO_BODY = 0x00000002; public static final int APPLIES_TO_DIV = 0x00000004; public static final int APPLIES_TO_P = 0x00000008; public static final int APPLIES_TO_SPAN = 0x00000010; public static final int APPLIES_TO_BR = 0x00000020; public static final int APPLIES_TO_CONTENT = (APPLIES_TO_BODY|APPLIES_TO_DIV|APPLIES_TO_P|APPLIES_TO_SPAN|APPLIES_TO_BR); public static final int APPLIES_TO_REGION = 0x00010000; public static final QName backgroundColorAttributeName = new QName(NAMESPACE,"backgroundColor"); public static final QName colorAttributeName = new QName(NAMESPACE,"color"); public static final QName directionAttributeName = new QName(NAMESPACE,"direction"); public static final QName displayAttributeName = new QName(NAMESPACE,"display"); public static final QName displayAlignAttributeName = new QName(NAMESPACE,"displayAlign"); public static final QName extentAttributeName = new QName(NAMESPACE,"extent"); public static final QName fontFamilyAttributeName = new QName(NAMESPACE,"fontFamily"); public static final QName fontSizeAttributeName = new QName(NAMESPACE,"fontSize"); public static final QName fontStyleAttributeName = new QName(NAMESPACE,"fontStyle"); public static final QName fontWeightAttributeName = new QName(NAMESPACE,"fontWeight"); public static final QName lineHeightAttributeName = new QName(NAMESPACE,"lineHeight"); public static final QName opacityAttributeName = new QName(NAMESPACE,"opacity"); public static final QName originAttributeName = new QName(NAMESPACE,"origin"); public static final QName overflowAttributeName = new QName(NAMESPACE,"overflow"); public static final QName paddingAttributeName = new QName(NAMESPACE,"padding"); public static final QName regionAttributeName = new QName("", "region"); public static final QName showBackgroundAttributeName = new QName(NAMESPACE,"showBackground"); public static final QName styleAttributeName = new QName("", "style"); public static final QName textAlignAttributeName = new QName(NAMESPACE,"textAlign"); public static final QName textDecorationAttributeName = new QName(NAMESPACE,"textDecoration"); public static final QName textOutlineAttributeName = new QName(NAMESPACE,"textOutline"); public static final QName unicodeBidiAttributeName = new QName(NAMESPACE,"unicodeBidi"); public static final QName visibilityAttributeName = new QName(NAMESPACE,"visibility"); public static final QName wrapOptionAttributeName = new QName(NAMESPACE,"wrapOption"); public static final QName writingModeAttributeName = new QName(NAMESPACE,"writingMode"); public static final QName zIndexAttributeName = new QName(NAMESPACE,"zIndex"); private static final Object[][] styleAccessorMap = new Object[][] { { backgroundColorAttributeName, // attribute name "BackgroundColor", // accessor method name suffix String.class, // value type BackgroundColorVerifier.class, // specialized verifier Integer.valueOf(APPLIES_TO_CONTENT|APPLIES_TO_REGION), // applicability Boolean.FALSE, // padding permitted Boolean.FALSE, // inheritable "transparent", // initial value (as object suitable for setter) null, // initial value as string (or null if same as previous) }, { colorAttributeName, "Color", String.class, ColorVerifier.class, Integer.valueOf(APPLIES_TO_SPAN), Boolean.FALSE, Boolean.TRUE, "white", null, }, { directionAttributeName, "Direction", Direction.class, DirectionVerifier.class, Integer.valueOf(APPLIES_TO_P|APPLIES_TO_SPAN), Boolean.FALSE, Boolean.TRUE, Direction.LTR, Direction.LTR.value(), }, { displayAttributeName, "Display", Display.class, DisplayVerifier.class, Integer.valueOf(APPLIES_TO_CONTENT|APPLIES_TO_REGION), Boolean.FALSE, Boolean.FALSE, Display.AUTO, Display.AUTO.value(), }, { displayAlignAttributeName, "DisplayAlign", DisplayAlign.class, DisplayAlignVerifier.class, Integer.valueOf(APPLIES_TO_REGION), Boolean.FALSE, Boolean.FALSE, DisplayAlign.BEFORE, DisplayAlign.BEFORE.value(), }, { extentAttributeName, "Extent", String.class, ExtentVerifier.class, Integer.valueOf(APPLIES_TO_TT|APPLIES_TO_REGION), Boolean.FALSE, Boolean.FALSE, "auto", null, }, { fontFamilyAttributeName, "FontFamily", String.class, FontFamilyVerifier.class, Integer.valueOf(APPLIES_TO_P|APPLIES_TO_SPAN), Boolean.TRUE, Boolean.TRUE, "default", null, }, { fontSizeAttributeName, "FontSize", String.class, FontSizeVerifier.class, Integer.valueOf(APPLIES_TO_P|APPLIES_TO_SPAN), Boolean.FALSE, Boolean.TRUE, "1c", null, }, { fontStyleAttributeName, "FontStyle", FontStyle.class, FontStyleVerifier.class, Integer.valueOf(APPLIES_TO_P|APPLIES_TO_SPAN), Boolean.FALSE, Boolean.TRUE, FontStyle.NORMAL, FontStyle.NORMAL.value(), }, { fontWeightAttributeName, "FontWeight", FontWeight.class, FontWeightVerifier.class, Integer.valueOf(APPLIES_TO_P|APPLIES_TO_SPAN), Boolean.FALSE, Boolean.TRUE, FontWeight.NORMAL, FontWeight.NORMAL.value(), }, { lineHeightAttributeName, "LineHeight", String.class, LineHeightVerifier.class, Integer.valueOf(APPLIES_TO_P), Boolean.FALSE, Boolean.TRUE, "normal", null, }, { opacityAttributeName, "Opacity", Float.class, OpacityVerifier.class, Integer.valueOf(APPLIES_TO_REGION), Boolean.FALSE, Boolean.FALSE, Float.valueOf(1.0F), "1.0", }, { originAttributeName, "Origin", String.class, OriginVerifier.class, Integer.valueOf(APPLIES_TO_REGION), Boolean.FALSE, Boolean.FALSE, "auto", null }, { overflowAttributeName, "Overflow", Overflow.class, OverflowVerifier.class, Integer.valueOf(APPLIES_TO_REGION), Boolean.FALSE, Boolean.FALSE, Overflow.HIDDEN, Overflow.HIDDEN.value(), }, { paddingAttributeName, "Padding", String.class, PaddingVerifier.class, Integer.valueOf(APPLIES_TO_REGION), Boolean.FALSE, Boolean.FALSE, "0px", null, }, { regionAttributeName, "Region", Object.class, RegionAttributeVerifier.class, Integer.valueOf(APPLIES_TO_CONTENT), Boolean.FALSE, Boolean.FALSE, null, null, }, { showBackgroundAttributeName, "ShowBackground", ShowBackground.class, ShowBackgroundVerifier.class, Integer.valueOf(APPLIES_TO_REGION), Boolean.FALSE, Boolean.FALSE, ShowBackground.ALWAYS, ShowBackground.ALWAYS.value(), }, { styleAttributeName, "StyleAttribute", List.class, StyleAttributeVerifier.class, Integer.valueOf(APPLIES_TO_CONTENT), Boolean.FALSE, Boolean.FALSE, null, null, }, { textAlignAttributeName, "TextAlign", TextAlign.class, TextAlignVerifier.class, Integer.valueOf(APPLIES_TO_P), Boolean.FALSE, Boolean.TRUE, TextAlign.START, TextAlign.START.value(), }, { textDecorationAttributeName, "TextDecoration", TextDecoration.class, TextDecorationVerifier.class, Integer.valueOf(APPLIES_TO_SPAN), Boolean.FALSE, Boolean.TRUE, TextDecoration.NONE, TextDecoration.NONE.value(), }, { textOutlineAttributeName, "TextOutline", String.class, TextOutlineVerifier.class, Integer.valueOf(APPLIES_TO_SPAN), Boolean.FALSE, Boolean.TRUE, "none", null, }, { unicodeBidiAttributeName, "UnicodeBidi", UnicodeBidi.class, UnicodeBidiVerifier.class, Integer.valueOf(APPLIES_TO_P|APPLIES_TO_SPAN), Boolean.FALSE, Boolean.FALSE, UnicodeBidi.NORMAL, UnicodeBidi.NORMAL.value(), }, { visibilityAttributeName, "Visibility", Visibility.class, VisibilityVerifier.class, Integer.valueOf(APPLIES_TO_CONTENT|APPLIES_TO_REGION), Boolean.FALSE, Boolean.TRUE, Visibility.VISIBLE, Visibility.VISIBLE.value(), }, { wrapOptionAttributeName, "WrapOption", WrapOption.class, WrapOptionVerifier.class, Integer.valueOf(APPLIES_TO_SPAN), Boolean.FALSE, Boolean.TRUE, WrapOption.WRAP, WrapOption.WRAP.value(), }, { writingModeAttributeName, "WritingMode", WritingMode.class, WritingModeVerifier.class, Integer.valueOf(APPLIES_TO_REGION), Boolean.FALSE, Boolean.FALSE, WritingMode.LRTB, WritingMode.LRTB.value(), }, { zIndexAttributeName, "ZIndex", String.class, ZIndexVerifier.class, Integer.valueOf(APPLIES_TO_REGION), Boolean.FALSE, Boolean.FALSE, "auto", null, }, }; protected Map<QName, StyleAccessor> accessors; public TTML1StyleVerifier(Model model) { super(model); populate(); } public QName getStyleAttributeName(String propertyName) { // assumes that property name is same as local part of qualified attribute name, which // is presently true in TTML1 for (QName name : accessors.keySet()) { if (propertyName.equals(name.getLocalPart())) return name; } return null; } public Collection<QName> getDefinedStyleNames() { return accessors.keySet(); } public Collection<QName> getApplicableStyleNames(QName eltName) { Collection<QName> names = new java.util.ArrayList<QName>(); for (Map.Entry<QName, StyleAccessor> e : accessors.entrySet()) { if (e.getValue().doesStyleApply(eltName)) names.add(e.getKey()); } return names; } public boolean isInheritableStyle(QName eltName, QName styleName) { if (accessors.containsKey(styleName)) return accessors.get(styleName).isInheritable(); else return false; } public String getInitialStyleValue(QName eltName, QName styleName) { if (accessors.containsKey(styleName)) return accessors.get(styleName).initialValueAsString(); else return null; } public boolean doesStyleApply(QName eltName, QName styleName) { if (accessors.containsKey(styleName)) { return accessors.get(styleName).doesStyleApply(eltName); } else return false; } public boolean isNegativeLengthPermitted(QName eltName, QName styleName) { return true; } public boolean isLengthUnitsPermitted(QName eltName, QName styleName, Length.Unit units) { return true; } public boolean isLengthUnitsRequired(QName eltName, QName styleName) { return true; } public boolean verify(Object content, Locator locator, VerifierContext context, ItemType type) { setState(content, context); if (type == ItemType.Attributes) return verifyAttributeItems(content, locator, context); else if (type == ItemType.Element) return verifyElementItem(content, locator, context); else if (type == ItemType.Other) return verifyOtherAttributes(content, locator, context); else throw new IllegalArgumentException(); } public void addInitialOverrides(Object initial, VerifierContext context) { } public Object getInitialOverride(QName styleName, VerifierContext context) { return null; } protected boolean verifyAttributeItems(Object content, Locator locator, VerifierContext context) { boolean failed = false; for (StyleAccessor sa : accessors.values()) { if (!verifyAttributeItem(content, locator, sa, context)) failed = true; } return !failed; } protected boolean verifyAttributeItem(Object content, Locator locator, StyleAccessor sa, VerifierContext context) { return sa.verify(getModel(), content, locator, context); } protected boolean verifyElementItem(Object content, Locator locator, VerifierContext context) { boolean failed = false; if (isSet(content)) failed = !verifySet(content, locator, context); if (failed) { Reporter reporter = context.getReporter(); reporter.logError(reporter.message(locator, "*KEY*", "Invalid ''{0}'' styled item.", context.getBindingElementName(content))); } return !failed; } protected boolean verifySet(Object content, Locator locator, VerifierContext context) { boolean failed = false; int numStyleAttributes = 0; NamedNodeMap attributes = context.getXMLNode(content).getAttributes(); for (int i = 0, n = attributes.getLength(); i < n; ++i) { Node attribute = attributes.item(i); String nsUri = attribute.getNamespaceURI(); if ((nsUri != null) && nsUri.equals(NAMESPACE)) ++numStyleAttributes; } if (numStyleAttributes > 1) { Reporter reporter = context.getReporter(); reporter.logInfo(reporter.message(locator, "*KEY*", "Style attribute count exceeds maximum, got {0}, expected no more than 1.", numStyleAttributes)); failed = true; } return !failed; } protected boolean verifyOtherAttributes(Object content, Locator locator, VerifierContext context) { boolean failed = false; NamedNodeMap attributes = context.getXMLNode(content).getAttributes(); for (int i = 0, n = attributes.getLength(); i < n; ++i) { Node item = attributes.item(i); if (!(item instanceof Attr)) continue; Attr attribute = (Attr) item; String nsUri = attribute.getNamespaceURI(); String localName = attribute.getLocalName(); if (localName == null) localName = attribute.getName(); if (localName.indexOf("xmlns") == 0) continue; QName name = new QName(nsUri != null ? nsUri : "", localName); if (name.getNamespaceURI().equals(NAMESPACE)) { Reporter reporter = context.getReporter(); if ((content instanceof TimedText) && name.equals(extentAttributeName)) continue; else if (!isStyleAttribute(name)) { reporter.logError(reporter.message(locator, "*KEY*", "Unknown attribute in TT Style namespace ''{0}'' not permitted on ''{1}''.", name, context.getBindingElementName(content))); failed = true; } else if (!permitsStyleAttribute(content, name)) { reporter.logError(reporter.message(locator, "*KEY*", "TT Style attribute ''{0}'' not permitted on ''{1}''.", name, context.getBindingElementName(content))); failed = true; } } } return !failed; } protected boolean permitsStyleAttribute(Object content, QName name) { if (content instanceof Body) return true; else if (content instanceof Division) return true; else if (content instanceof Paragraph) return true; else if (content instanceof Span) return true; else if (content instanceof Break) return true; else if (content instanceof Style) return true; else if (content instanceof Region) return true; else if (content instanceof Set) return true; else return false; } protected boolean isInitial(Object content) { return false; } protected boolean isRegion(Object content) { return content instanceof Region; } protected boolean isSet(Object content) { return content instanceof Set; } protected boolean isTimedText(Object content) { return content instanceof TimedText; } protected boolean isStyleAttribute(QName name) { return name.getNamespaceURI().equals(NAMESPACE) && accessors.containsKey(name); } private void populate() { this.accessors = makeAccessors(); } private Map<QName, StyleAccessor> makeAccessors() { Map<QName, StyleAccessor> accessors = new java.util.HashMap<QName, StyleAccessor>(); populateAccessors(accessors); return accessors; } protected void populateAccessors(Map<QName, StyleAccessor> accessors) { populateAccessors(accessors, styleAccessorMap); } protected void populateAccessors(Map<QName, StyleAccessor> accessors, Object[][] accessorMap) { for (Object[] styleAccessorEntry : accessorMap) { assert styleAccessorEntry.length >= 9; QName styleName = (QName) styleAccessorEntry[0]; String accessorName = (String) styleAccessorEntry[1]; Class<?> valueClass = (Class<?>) styleAccessorEntry[2]; Class<?> verifierClass = (Class<?>) styleAccessorEntry[3]; int applicability = ((Integer) styleAccessorEntry[4]).intValue(); boolean paddingPermitted = ((Boolean) styleAccessorEntry[5]).booleanValue(); boolean inheritable = ((Boolean) styleAccessorEntry[6]).booleanValue(); Object initialValue = styleAccessorEntry[7]; String initialValueAsString = (String) styleAccessorEntry[8]; if (initialValueAsString == null) initialValueAsString = (initialValue != null) ? initialValue.toString() : null; accessors.put(styleName, new StyleAccessor(styleName, accessorName, valueClass, verifierClass, applicability, paddingPermitted, inheritable, initialValue, initialValueAsString)); } } protected void setStyleInitialValue(Object content, StyleAccessor sa, Object initialValue, VerifierContext context) { // defer initial value setting to ISD generation (ttx) } protected void setStyleValue(Object content, String setterName, Class<?> valueClass, Object value) { try { Class<?> contentClass = content.getClass(); Method m = contentClass.getMethod(setterName, new Class<?>[]{ valueClass }); m.invoke(content, new Object[]{ value }); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (SecurityException e) { throw new RuntimeException(e); } } protected class StyleAccessor { QName styleName; String getterName; String setterName; Class<?> valueClass; StyleValueVerifier verifier; int applicability; boolean paddingPermitted; boolean inheritable; Object initialValue; String initialValueAsString; public StyleAccessor(QName styleName, String accessorName, Class<?> valueClass, Class<?> verifierClass, int applicability, boolean paddingPermitted, boolean inheritable, Object initialValue, String initialValueAsString) { populate(styleName, accessorName, valueClass, verifierClass, applicability, paddingPermitted, inheritable, initialValue, initialValueAsString); } public QName getStyleName() { return styleName; } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append('['); sb.append(styleName); sb.append(','); sb.append(initialValue != null ? initialValue.toString() : ""); sb.append(','); sb.append(inheritable); sb.append(']'); return sb.toString(); } public boolean doesStyleApply(QName eltName) { String nsUri = eltName.getNamespaceURI(); if ((nsUri == null) || !nsUri.equals(NAMESPACE_TT)) { return false; } else { String localName = eltName.getLocalPart(); if (localName.equals("tt")) return (applicability & APPLIES_TO_TT) == APPLIES_TO_TT; else if (localName.equals("body")) return (applicability & APPLIES_TO_BODY) == APPLIES_TO_BODY; else if (localName.equals("div")) return (applicability & APPLIES_TO_DIV) == APPLIES_TO_DIV; else if (localName.equals("p")) return (applicability & APPLIES_TO_P) == APPLIES_TO_P; else if (localName.equals("span")) return (applicability & APPLIES_TO_SPAN) == APPLIES_TO_SPAN; else if (localName.equals("br")) return (applicability & APPLIES_TO_BR) == APPLIES_TO_BR; else if (localName.equals("region")) return (applicability & APPLIES_TO_REGION) == APPLIES_TO_REGION; else return false; } } public boolean isInheritable() { return inheritable; } public String initialValueAsString() { return initialValueAsString; } protected boolean verify(Model model, Object content, Locator locator, VerifierContext context) { boolean success = true; Object value = getStyleValue(content); if (value != null) { Location location = new Location(content, context.getBindingElementName(content), styleName, locator); if (value instanceof String) success = verify((String) value, location, context); else success = verifier.verify(value, location, context); } else if (!isInitial(content)) setStyleInitialValue(content, context); if (!success) { if (value != null) { if (styleName.equals(styleAttributeName)) { value = IdReferences.getIdReferences(value); } else if (styleName.equals(regionAttributeName)) { value = IdReferences.getIdReference(value); } else value = value.toString(); Reporter reporter = context.getReporter(); reporter.logError(reporter.message(locator, "*KEY*", "Invalid {0} value ''{1}''.", styleName, value)); } } return success; } protected boolean verify(String value, Location location, VerifierContext context) { boolean success = false; Reporter reporter = context.getReporter(); Locator locator = location.getLocator(); if (value.length() == 0) { reporter.logInfo(reporter.message(locator, "*KEY*", "Empty {0} not permitted, got ''{1}''.", styleName, value)); } else if (Strings.isAllXMLSpace(value)) { reporter.logInfo(reporter.message(locator, "*KEY*", "The value of {0} is entirely XML space characters, got ''{1}''.", styleName, value)); } else if (!paddingPermitted && !value.equals(value.trim())) { reporter.logInfo(reporter.message(locator, "*KEY*", "XML space padding not permitted on {0}, got ''{1}''.", styleName, value)); } else success = verifier.verify(value, location, context); return success; } private StyleValueVerifier createStyleValueVerifier(Class<?> verifierClass) { try { return (StyleValueVerifier) verifierClass.newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } private void populate(QName styleName, String accessorName, Class<?> valueClass, Class<?> verifierClass, int applicability, boolean paddingPermitted, boolean inheritable, Object initialValue, String initialValueAsString) { this.styleName = styleName; this.getterName = "get" + accessorName; this.setterName = "set" + accessorName; this.valueClass = valueClass; this.verifier = createStyleValueVerifier(verifierClass); this.paddingPermitted = paddingPermitted; this.applicability = applicability; this.inheritable = inheritable; this.initialValue = initialValue; this.initialValueAsString = initialValueAsString; } public Object getStyleValue(Object content) { try { Class<?> contentClass = content.getClass(); Method m = contentClass.getMethod(getterName, new Class<?>[]{}); return convertType(m.invoke(content, new Object[]{}), valueClass); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { return null; } catch (SecurityException e) { throw new RuntimeException(e); } } private void setStyleInitialValue(Object content, VerifierContext context) { TTML1StyleVerifier.this.setStyleInitialValue(content, this, initialValue, context); } private Object convertType(Object value, Class<?> targetClass) { if (value == null) return null; else if (targetClass.isInstance(value)) return value; else if (value.getClass() == String.class) { if (targetClass == Float.class) { try { return Float.valueOf((String) value); } catch (NumberFormatException e) { return null; } } else return null; } else if (value.getClass() == Float.class) { if (targetClass == String.class) return ((Float) value).toString(); else return null; } else if (value instanceof Enum<?>) { if (targetClass == String.class) return Enums.getValue((Enum<?>) value); else return null; } else return null; } } public static final String getStyleNamespaceUri() { return NAMESPACE; } }