All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jsimpledb.schema.AbstractSchemaItem Maven / Gradle / Ivy


/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package org.jsimpledb.schema;

import com.google.common.base.Preconditions;

import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiPredicate;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

import org.jsimpledb.core.InvalidSchemaException;
import org.jsimpledb.core.SchemaItem;
import org.jsimpledb.util.Diffs;

/**
 * Common superclass for {@link SchemaObjectType} and {@link SchemaField}.
 */
public abstract class AbstractSchemaItem extends SchemaSupport {

    private String name;
    private int storageId;

    /**
     * Get the name associated with this instance, if any.
     *
     * @return the name of this instance, or null if it has none
     */
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.verifyNotLockedDown();
        this.name = name;
    }

    /**
     * Get the storage ID associated with this instance.
     * Storage IDs must be positive values.
     *
     * @return the storage ID for this instance
     */
    public int getStorageId() {
        return this.storageId;
    }
    public void setStorageId(int storageId) {
        this.verifyNotLockedDown();
        this.storageId = storageId;
    }

// Validation

    void validate() {
        if (name == null)
            throw new InvalidSchemaException(this + " must specify a name");
        if (!name.matches(SchemaItem.NAME_PATTERN))
            throw new InvalidSchemaException(this + " has an invalid name `" + name + "'");
        if (this.storageId <= 0)
            throw new InvalidSchemaException(this + " has an invalid storage ID " + this.storageId + "; must be greater than zero");
    }

// Compatibility

    static  boolean isAll(Map map1, Map map2, BiPredicate checker) {
        if (!map1.keySet().equals(map2.keySet()))
            return false;
        for (Map.Entry entry : map1.entrySet()) {
            final K key = entry.getKey();
            final V value1 = entry.getValue();
            final V value2 = map2.get(key);
            if (value1 == null || value2 == null || !checker.test(value1, value2))
                return false;
        }
        return true;
    }

    void writeCompatibilityHashData(DataOutputStream output) throws IOException {
        output.writeUTF(this.getClass().getSimpleName());
        output.writeInt(this.storageId);
    }

// DiffGenerating

    protected Diffs differencesFrom(AbstractSchemaItem that) {
        Preconditions.checkArgument(that != null, "null that");
        final Diffs diffs = new Diffs();
        if (!(this.name != null ? this.name.equals(that.name) : that.name == null)) {
            diffs.add("changed name from " + (that.name != null ? "`" + that.name + "'" : null)
              + " to " + (this.name != null ? "`" + this.name + "'" : null));
        }
        if (this.storageId != that.storageId)
            diffs.add("changed storage ID from " + that.storageId + " to " + this.storageId);
        return diffs;
    }

// XML Reading

    /**
     * Read in this item's XML.
     *
     * 

* The implementation in {@link AbstractSchemaItem} invokes {@link #readAttributes readAttributes()} * followed by {@link #readSubElements readSubElements()}. * *

* Start state: positioned at opening XML tag. * Return state: positioned at closing XML tag. */ void readXML(XMLStreamReader reader, int formatVersion) throws XMLStreamException { this.readAttributes(reader, formatVersion); this.readSubElements(reader, formatVersion); } /** * Read in this item's start tag attributes. * *

* The implementation in {@link AbstractSchemaItem} reads in required storage ID and name attributes. * *

* Start state: positioned at opening XML tag. * Return state: same. */ void readAttributes(XMLStreamReader reader, int formatVersion) throws XMLStreamException { final Integer storageIdAttr = this.getIntAttr(reader, XMLConstants.STORAGE_ID_ATTRIBUTE, false); if (storageIdAttr != null) this.setStorageId(storageIdAttr); final String nameAttr = this.getAttr(reader, XMLConstants.NAME_ATTRIBUTE, false); if (nameAttr != null) this.setName(nameAttr); } /** * Read in this item's sub-elements. * *

* The implementation in {@link AbstractSchemaItem} expects no sub-elements. * *

* Start state: positioned at opening XML tag. * Return state: positioned at closing XML tag. */ void readSubElements(XMLStreamReader reader, int formatVersion) throws XMLStreamException { this.expectClose(reader); } /** * Read an element found in the given map. * * @return element found, or null if closing XML tag encountered instead */ T readMappedType(XMLStreamReader reader, boolean closingOK, Map> tagMap) throws XMLStreamException { // Expect to see one of the map's XML tag keys if (!this.expect(reader, closingOK, tagMap.keySet().toArray(new QName[tagMap.size()]))) return null; // Instantiate the corresponding type T obj = null; for (Map.Entry> entry : tagMap.entrySet()) { if (reader.getName().equals(entry.getKey())) { try { return entry.getValue().newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException("unexpected exception", e); } } } throw new RuntimeException("internal error: didn't find " + reader.getName() + " in tagMap"); } /** * Read an {@link Enum} attribute. * * @param reader XML reader * @param type {@link Enum} type * @param name attribute name * @param defaultValue default value, or null if value is required */ > T readAttr(XMLStreamReader reader, Class type, QName name, T defaultValue) throws XMLStreamException { final String text = this.getAttr(reader, name, defaultValue == null); if (text == null) return defaultValue; try { return Enum.valueOf(type, text); } catch (IllegalArgumentException e) { throw new XMLStreamException("invalid value `" + text + " for \"" + name.getLocalPart() + "\" attribute in " + this, reader.getLocation()); } } // XML Writing abstract void writeXML(XMLStreamWriter writer) throws XMLStreamException; final void writeAttributes(XMLStreamWriter writer) throws XMLStreamException { this.writeAttributes(writer, true); } void writeAttributes(XMLStreamWriter writer, boolean includeName) throws XMLStreamException { writer.writeAttribute(XMLConstants.STORAGE_ID_ATTRIBUTE.getNamespaceURI(), XMLConstants.STORAGE_ID_ATTRIBUTE.getLocalPart(), "" + this.storageId); if (includeName && this.name != null) { writer.writeAttribute(XMLConstants.NAME_ATTRIBUTE.getNamespaceURI(), XMLConstants.NAME_ATTRIBUTE.getLocalPart(), this.name); } } // Object @Override public String toString() { return "#" + this.storageId + (this.name != null ? " `" + this.name + "'" : ""); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || obj.getClass() != this.getClass()) return false; final AbstractSchemaItem that = (AbstractSchemaItem)obj; return Objects.equals(this.name, that.name) && this.storageId == that.storageId; } @Override public int hashCode() { return Objects.hashCode(this.name) ^ this.storageId; } // Cloneable @Override protected AbstractSchemaItem clone() { return (AbstractSchemaItem)super.clone(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy