/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.waveprotocol.wave.model.schema;
import org.waveprotocol.wave.model.document.operation.automaton.DocumentSchema;
import org.waveprotocol.wave.model.util.CollectionUtils;
import org.waveprotocol.wave.model.util.Preconditions;
import org.waveprotocol.wave.model.util.StringMap;
import org.waveprotocol.wave.model.util.StringSet;
import java.util.Collections;
import java.util.List;
/**
* Helper base class for somewhat simplifying the chore of defining schema
* constraints, while allowing any method to be overridden for Turing complete
* schema validation goodness.
*
* @author danilatos@google.com (Daniel Danilatos)
*/
public abstract class AbstractXmlSchemaConstraints implements DocumentSchema {
private static final String IMPLICIT_ROOT_TYPE = "";
// Signals an error if type is IMPLICIT_ROOT_TYPE, and translates null to IMPLICIT_ROOT_TYPE.
// We do this because we can't use null as a key in StringMaps.
private static String fixType(String type) {
Preconditions.checkArgument(!IMPLICIT_ROOT_TYPE.equals(type), "Invalid type");
if (type == null) {
return IMPLICIT_ROOT_TYPE;
} else {
return type;
}
}
private static void checkNotTopLevel(String type) {
Preconditions.checkArgument(!IMPLICIT_ROOT_TYPE.equals(type), "Invalid type");
Preconditions.checkNotNull(type, "Null type");
}
private final StringMap<StringSet> permittedChildren = CollectionUtils.createStringMap();
private final StringMap<StringMap<StringSet>> permittedAttrs = CollectionUtils.createStringMap();
private final StringMap<PermittedCharacters> textPermitted = CollectionUtils.createStringMap();
private final StringMap<List<String>> requiredInitial = CollectionUtils.createStringMap();
public void addRequiredInitial(String key, List<String> list) {
requiredInitial.put(key, list);
}
public void containsBlipText(String ... types) {
for (String type : types) {
type = fixType(type);
textPermitted.put(type, PermittedCharacters.BLIP_TEXT);
}
}
public void containsAnyText(String ... types) {
for (String type : types) {
type = fixType(type);
textPermitted.put(type, PermittedCharacters.ANY);
}
}
public void addChildren(String parentType, String ... childTypes) {
parentType = fixType(parentType);
StringSet permitted = permittedChildren.get(parentType);
if (permitted == null) {
permittedChildren.put(parentType, permitted = CollectionUtils.createStringSet());
}
for (String childType : childTypes) {
checkNotTopLevel(childType);
permitted.add(childType);
}
}
public void addAttrs(String type, String ... attrNames) {
checkNotTopLevel(type);
for (String name : attrNames) {
addAttrWithValues(type, name);
}
}
public void addAttrWithValues(String type, String attrName, String ... permittedValues) {
checkNotTopLevel(type);
StringMap<StringSet> attrs = permittedAttrs.get(type);
if (attrs == null) {
permittedAttrs.put(type, attrs = CollectionUtils.createStringMap());
}
if (permittedValues.length == 0) {
attrs.put(attrName, null);
} else {
StringSet values = attrs.get(attrName);
if (values == null) {
attrs.put(attrName, values = CollectionUtils.createStringSet());
}
for (String value : permittedValues) {
values.add(value);
}
}
}
@Override
public boolean permitsAttribute(String type, String attr) {
checkNotTopLevel(type);
StringMap<StringSet> attrs = permittedAttrs.get(type);
return attrs != null && (attrs.containsKey(attr));
}
@Override
public boolean permitsAttribute(String type, String attr, String value) {
checkNotTopLevel(type);
StringMap<StringSet> attrs = permittedAttrs.get(type);
return attrs != null && attrs.containsKey(attr) &&
(attrs.get(attr) == null || attrs.get(attr).contains(value));
}
@Override
public boolean permitsChild(String parentType, String childType) {
parentType = fixType(parentType);
checkNotTopLevel(childType);
StringSet permitted = permittedChildren.get(parentType);
return permitted != null && permitted.contains(childType);
}
@Override
public PermittedCharacters permittedCharacters(String typeOrNull) {
typeOrNull = fixType(typeOrNull);
PermittedCharacters permitted = textPermitted.get(typeOrNull);
return permitted != null ? permitted : PermittedCharacters.NONE;
}
@Override
public List<String> getRequiredInitialChildren(String type) {
type = fixType(type);
return requiredInitial.containsKey(type)
? requiredInitial.get(type) : Collections.<String>emptyList();
}
}