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

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

Go to download

JSimpleDB core API classes which provide objects, fields, indexes, queries, and schema management on top of a key/value store.

There is a newer version: 3.6.1
Show newest version

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

package org.jsimpledb.schema;

import com.google.common.base.Preconditions;

import java.util.Comparator;
import java.util.Map;

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.AbstractXMLStreaming;
import org.jsimpledb.util.Diffs;

/**
 * Common superclass for {@link SchemaObjectType} and {@link SchemaField}.
 */
public abstract class AbstractSchemaItem extends AbstractXMLStreaming implements XMLConstants, Cloneable {

    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.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.storageId = storageId;
    }

    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");
    }

    /**
     * Determine whether this instance is compatible with the given instance for use with the core API.
     * Two instances are compatible if they are identical in all respects except for object and field names
     * (to also include object and field names in the comparison, use {@link #equals equals()}).
     * The core API uses storage IDs, not names, to identify objects and fields.
     *
     * @param that other schema object
     * @return true if this and {@code that} are compatible
     * @throws IllegalArgumentException if {@code that} is null
     */
    public final boolean isCompatibleWith(AbstractSchemaItem that) {
        Preconditions.checkArgument(that != null, "null that");
        if (this.storageId != that.storageId)
            return false;
        if (this.getClass() != that.getClass())
            return false;
        return this.isCompatibleWithInternal(that);
    }

    abstract boolean isCompatibleWithInternal(AbstractSchemaItem that);

// 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, STORAGE_ID_ATTRIBUTE, false); if (storageIdAttr != null) this.setStorageId(storageIdAttr); final String nameAttr = this.getAttr(reader, 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"); } // 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(STORAGE_ID_ATTRIBUTE.getNamespaceURI(), STORAGE_ID_ATTRIBUTE.getLocalPart(), "" + this.storageId); if (includeName && this.name != null) writer.writeAttribute(NAME_ATTRIBUTE.getNamespaceURI(), 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 (this.name != null ? this.name.equals(that.name) : that.name == null) && this.storageId == that.storageId; } @Override public int hashCode() { return (this.name != null ? this.name.hashCode() : 0) ^ this.storageId; } // Cloneable /** * Deep-clone this instance. */ @Override public AbstractSchemaItem clone() { try { return (AbstractSchemaItem)super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } // NameComparator static class NameComparator implements Comparator { @Override public int compare(AbstractSchemaItem item1, AbstractSchemaItem item2) { return item1.getName().compareTo(item2.getName()); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy