org.wrml.runtime.schema.SchemaBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.wrml.runtime.schema.SchemaBuilder.java

Source

/**
 * WRML - Web Resource Modeling Language
 *  __     __   ______   __    __   __
 * /\ \  _ \ \ /\  == \ /\ "-./  \ /\ \
 * \ \ \/ ".\ \\ \  __< \ \ \-./\ \\ \ \____
 *  \ \__/".~\_\\ \_\ \_\\ \_\ \ \_\\ \_____\
 *   \/_/   \/_/ \/_/ /_/ \/_/  \/_/ \/_____/
 *
 * http://www.wrml.org
 *
 * Copyright (C) 2011 - 2013 Mark Masse <mark@wrml.org> (OSS project WRML.org)
 *
 * Licensed 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.wrml.runtime.schema;

import org.apache.commons.lang3.StringUtils;
import org.wrml.model.rest.LinkRelation;
import org.wrml.model.schema.*;
import org.wrml.runtime.Context;
import org.wrml.runtime.Dimensions;
import org.wrml.runtime.Keys;
import org.wrml.runtime.KeysBuilder;
import org.wrml.runtime.syntax.SyntaxLoader;
import org.wrml.util.UniqueName;

import java.lang.reflect.Type;
import java.net.URI;
import java.util.*;

/**
 * A helper utility to build Schema models from everyday inputs (e.g. <code>Map<String, Object></code>).
 */
public final class SchemaBuilder {

    private static final String JAVA_KEYWORDS[] = { "abstract", "assert", "boolean", "break", "byte", "case",
            "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "extends", "false",
            "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int",
            "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return",
            "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws",
            "transient", "true", "try", "void", "volatile", "while" };

    private static final SortedSet<String> RESERVED_WORD_SET = new TreeSet<>();

    static {
        for (final String javaKeyword : JAVA_KEYWORDS) {
            RESERVED_WORD_SET.add(javaKeyword);
        }
    }

    private final Context _Context;

    private final Schema _Schema;

    private final Map<String, Slot> _Slots;

    public SchemaBuilder(final Context context) {

        this(context, (Map<String, Object>) null);
    }

    public SchemaBuilder(final Context context, final Object... slots) {

        this(context, (Map<String, Object>) null);
        slots(slots);
    }

    public SchemaBuilder(final Context context, final Map<String, Object> startFromSlotMap) {

        this(context, startFromSlotMap, null);
    }

    public SchemaBuilder(final Context context, final Map<String, Object> startFromSlotMap, final UniqueName name) {

        if (context == null) {
            throw new IllegalArgumentException("Context cannot be null.");
        }

        _Context = context;
        _Schema = _Context.newModel(Schema.class);

        if (name != null) {
            _Schema.setUniqueName(name);
        }

        _Slots = new TreeMap<>();

        if (startFromSlotMap != null) {
            slots(startFromSlotMap);
        }
    }

    public static String ensureValidJavaIdentifier(final String identifier) {

        if (identifier == null || RESERVED_WORD_SET.contains(identifier)) {
            return null;
        }

        String validJavaIdentifier = identifier;

        int index = validJavaIdentifier.indexOf('-');
        if (index > 0) {
            validJavaIdentifier = validJavaIdentifier.replace('-', '_');
        }

        if (!Character.isJavaIdentifierStart(validJavaIdentifier.charAt(0))) {
            validJavaIdentifier = "_" + validJavaIdentifier;
        }

        final int validJavaIdentifierLength = validJavaIdentifier.length();
        String replaceCharacters = "";
        for (int i = 1; i < validJavaIdentifierLength; i++) {
            char c = validJavaIdentifier.charAt(i);
            if (!Character.isJavaIdentifierPart(c)) {
                replaceCharacters += c;
            }
        }

        if (!replaceCharacters.isEmpty()) {
            validJavaIdentifier = StringUtils.replaceChars(validJavaIdentifier, replaceCharacters, "$");
        }

        return validJavaIdentifier;
    }

    public SchemaBuilder extend(final URI baseSchemaUri, final URI... baseSchemaUris) {

        _Schema.getBaseSchemaUris().add(baseSchemaUri);

        if (baseSchemaUris != null) {
            _Schema.getBaseSchemaUris().addAll(Arrays.asList(baseSchemaUris));
        }

        return this;
    }

    public SchemaBuilder extend(final Schema baseSchema, final Schema... baseSchemas) {

        extend(baseSchema.getUri());

        if (baseSchemas != null) {
            final LinkedHashSet<URI> baseSchemaUris = new LinkedHashSet<>(baseSchemas.length);
            for (final Schema schema : baseSchemas) {
                baseSchemaUris.add(schema.getUri());
            }

            _Schema.getBaseSchemaUris().addAll(baseSchemaUris);
        }

        return this;
    }

    public SchemaBuilder extend(final Class<?> baseSchemaInterface, final Class<?>... baseSchemaInterfaces) {

        final SchemaLoader schemaLoader = _Context.getSchemaLoader();
        extend(schemaLoader.getTypeUri(baseSchemaInterface));

        if (baseSchemaInterfaces != null) {
            final LinkedHashSet<URI> baseSchemaUris = new LinkedHashSet<>(baseSchemaInterfaces.length);
            for (final Class<?> schemaInterface : baseSchemaInterfaces) {
                baseSchemaUris.add(schemaLoader.getTypeUri(schemaInterface));
            }

            _Schema.getBaseSchemaUris().addAll(baseSchemaUris);
        }

        return this;
    }

    public SchemaBuilder extend(final Collection<URI> baseSchemaUris) {

        _Schema.getBaseSchemaUris().addAll(baseSchemaUris);
        return this;
    }

    public SchemaBuilder slots(final Map<String, Object> slotMap) {

        final Set<String> slotNames = slotMap.keySet();
        for (final String slotName : slotNames) {
            slot(slotName, slotMap.get(slotName));
        }

        return this;
    }

    public SchemaBuilder slots(final Object... slots) {

        if (slots == null) {
            throw new IllegalArgumentException("The slots cannot be null.");
        }

        if ((slots.length % 2) != 0) {
            throw new IllegalArgumentException(
                    "The slots must be in name/value pair order. Slots: " + Arrays.deepToString(slots));
        }

        for (int i = 0; i < slots.length; i++) {

            if (!(slots[i] instanceof String)) {
                throw new IllegalArgumentException(
                        "The slot's _definition_ array must contain a (String) name and (Object) value. Slots: "
                                + Arrays.deepToString(slots));
            }

            final String slotName = (String) slots[i];

            i = i + 1;

            final Object slotValue = slots[i];

            slot(slotName, slotValue);
        }

        return this;
    }

    public SchemaBuilder slot(final String slotName, final Object slotValue) {

        if (slotName == null) {
            throw new IllegalArgumentException("Slot name cannot be null.");
        }

        final String validName = ensureValidJavaIdentifier(slotName);

        if (validName == null) {
            throw new IllegalArgumentException("Invalid slot name: " + slotName);
        }

        final ProtoSlot protoSlot = _Schema.getPrototype().getProtoSlot(validName, false);
        if (protoSlot != null) {
            _Schema.setSlotValue(validName, slotValue);
            return this;
        }

        final Slot slot = _Context.newModel(Slot.class);
        slot.setName(validName);

        final Value value = createValue(slotValue);
        if (value == null) {
            throw new IllegalArgumentException("Invalid slot value: " + slotValue);
        }

        slot.setValue(value);

        _Slots.put(validName, slot);
        _Schema.getSlots().add(slot);

        return this;
    }

    public SchemaBuilder link(final LinkRelation linkRelation) {

        return link(linkRelation.getUri(), linkRelation.getUniqueName().getLocalName());
    }

    public SchemaBuilder link(final URI linkRelationUri) {

        final SchemaLoader schemaLoader = _Context.getSchemaLoader();
        final Keys keys = new KeysBuilder().addKey(schemaLoader.getDocumentSchemaUri(), linkRelationUri).toKeys();
        final Dimensions dimensions = schemaLoader.getLinkRelationDimensions();
        final LinkRelation linkRelation = _Context.getModel(keys, dimensions);
        return link(linkRelation);
    }

    public SchemaBuilder link(final URI linkRelationUri, final String linkSlotName) {

        return link(linkRelationUri, linkSlotName, null);
    }

    public SchemaBuilder link(final URI linkRelationUri, final String linkSlotName, final URI responseSchemaUri) {

        return link(linkRelationUri, linkSlotName, responseSchemaUri, false);
    }

    public SchemaBuilder link(final URI linkRelationUri, final String linkSlotName, final URI responseSchemaUri,
            final boolean embedded) {

        if (linkRelationUri == null) {
            throw new IllegalArgumentException("Link Relation URI cannot be null.");
        }

        if (linkSlotName == null) {
            throw new IllegalArgumentException("Link slot name cannot be null.");
        }

        final String validName = ensureValidJavaIdentifier(linkSlotName);

        if (validName == null) {
            throw new IllegalArgumentException("Invalid link slot name: " + linkSlotName);
        }

        final Slot slot = _Context.newModel(Slot.class);
        slot.setName(validName);

        final LinkValue linkValue = _Context.newModel(LinkValue.class);
        linkValue.setLinkRelationUri(linkRelationUri);
        if (responseSchemaUri != null) {
            linkValue.setResponseSchemaUri(responseSchemaUri);
        }

        linkValue.setEmbedded(embedded);

        slot.setValue(linkValue);

        _Slots.put(validName, slot);
        _Schema.getSlots().add(slot);
        return this;

    }

    public SchemaBuilder key(final String keySlotName) {

        return keys(keySlotName, (String[]) null);
    }

    public SchemaBuilder keys(final String keySlotName, final String... keySlotNames) {

        if (keySlotName == null) {
            throw new IllegalArgumentException("Key slot name cannot be null.");
        }

        _Schema.getKeySlotNames().add(keySlotName);

        if (keySlotNames != null) {
            for (final String slotName : keySlotNames) {
                if (slotName == null) {
                    throw new IllegalArgumentException("Key slot name cannot be null.");
                }

                _Schema.getKeySlotNames().add(slotName);
            }
        }

        return this;
    }

    public SchemaBuilder keys(final Collection<String> keySlotNames) {

        _Schema.getKeySlotNames().addAll(keySlotNames);
        return this;
    }

    public Schema toSchema() {

        return _Schema;
    }

    public Context getContext() {

        return _Context;
    }

    public Slot getSlot(final String slotName) {

        if (!_Slots.containsKey(slotName)) {
            return null;
        }

        return _Slots.get(slotName);
    }

    public Class<?> load() throws ClassNotFoundException {

        final SchemaLoader schemaLoader = _Context.getSchemaLoader();
        final Schema schema = toSchema();
        schemaLoader.load(schema);
        return schemaLoader.getSchemaInterface(schema.getUri());
    }

    private Value createValue(final ValueType valueType) {

        return createValue(valueType, null);
    }

    private Value createValue(final Object value) {

        final Value slotValue;
        if (value instanceof ValueType) {
            slotValue = createValue((ValueType) value);
        } else if (value instanceof Class<?>) {
            slotValue = createValue((Class<?>) value);
        } else {
            slotValue = createValue(value.getClass());

            if (slotValue != null
                    && slotValue.getPrototype().getProtoSlot(Value.SLOT_NAME_DEFAULT, false) != null) {
                slotValue.setSlotValue(Value.SLOT_NAME_DEFAULT, value);
            }
        }

        return slotValue;
    }

    private Value createValue(final Class<?> heapValueType) {

        final SchemaLoader schemaLoader = _Context.getSchemaLoader();
        final ValueType valueType = schemaLoader.getValueType(heapValueType);
        return createValue(valueType, heapValueType);
    }

    private Value createValue(final ValueType valueType, final Class<?> heapValueType) {

        final SchemaLoader schemaLoader = _Context.getSchemaLoader();
        final SyntaxLoader syntaxLoader = _Context.getSyntaxLoader();

        Value value = null;

        switch (valueType) {
        case Boolean:
            final BooleanValue booleanValue = _Context.newModel(BooleanValue.class);
            value = booleanValue;
            break;

        case Date:
            final DateValue dateValue = _Context.newModel(DateValue.class);
            value = dateValue;

            break;

        case Double:
            final DoubleValue doubleValue = _Context.newModel(DoubleValue.class);
            value = doubleValue;
            break;

        case Integer:
            final IntegerValue integerValue = _Context.newModel(IntegerValue.class);
            value = integerValue;
            break;

        case Model:

            final ModelValue modelValue = _Context.newModel(ModelValue.class);
            if (heapValueType != null) {
                final URI modelSchemaUri = schemaLoader.getTypeUri(heapValueType);
                modelValue.setModelSchemaUri(modelSchemaUri);
            }

            value = modelValue;
            break;

        case List:
            final ListValue listValue = _Context.newModel(ListValue.class);

            final Slot elementSlot = _Context.newModel(Slot.class);

            elementSlot.setName("E");

            final Type elementValueHeapType = ValueType.getListElementType(heapValueType);
            final Value elementValue = createValue(elementValueHeapType);
            elementSlot.setValue(elementValue);
            listValue.setElementSlot(elementSlot);

            value = listValue;
            break;

        case Long:
            final LongValue longValue = _Context.newModel(LongValue.class);
            value = longValue;
            break;

        case SingleSelect:
            final SingleSelectValue singleSelectValue = _Context.newModel(SingleSelectValue.class);
            value = singleSelectValue;
            break;

        case Text:
            final TextValue textValue = _Context.newModel(TextValue.class);

            if (heapValueType != null && !String.class.equals(heapValueType)) {
                final URI syntaxUri = syntaxLoader.getSyntaxUri(heapValueType);
                textValue.setSyntaxUri(syntaxUri);
            }

            value = textValue;

            break;

        default:
            break;

        }

        return value;

    }

}