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

io.permazen.core.Schemas Maven / Gradle / Ivy


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

package io.permazen.core;

import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;

import io.permazen.core.type.ReferenceFieldType;
import io.permazen.kv.KeyRanges;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * Contains the set of all {@link Schema}s of objects visible in a {@link Transaction}.
 * All database objects have an implicit schema version number, which corresponds to one of the {@link Schema}s
 * contained here and describes the object's fields.
 *
 * 

* Associated with a {@link Transaction} is a {@linkplain Transaction#getSchema specific schema version} * that the transaction uses to access objects. */ public class Schemas { final TreeMap versions = new TreeMap<>(); final TreeMap storageInfos = new TreeMap<>(); final ArrayList> referenceFieldIndexStorageInfos = new ArrayList<>(); final TreeSet objTypeStorageIds = new TreeSet<>(); KeyRanges objTypesKeyRanges; Schemas(SortedMap versions) { this.initialize(versions); } private void initialize(SortedMap versions) { // Sanity check Preconditions.checkArgument(versions != null, "null versions"); // Verify versions have the right version numbers for (Map.Entry entry : versions.entrySet()) { final int versionNumber = entry.getKey(); final Schema schema = entry.getValue(); Preconditions.checkArgument(schema != null, "null schema"); if (schema.versionNumber != versionNumber) { throw new InvalidSchemaException("schema version at index " + versionNumber + " has version " + schema.versionNumber); } } // Reset state this.versions.clear(); this.storageInfos.clear(); this.objTypeStorageIds.clear(); // Copy versions this.versions.putAll(versions); // Verify all schema versions use storage IDs in a compatible way final HashMap versionMap = new HashMap<>(); for (Schema version : this.versions.values()) { for (Map.Entry entry : version.storageInfoMap.entrySet()) { final int storageId = entry.getKey(); final StorageInfo current = entry.getValue(); final StorageInfo previous = this.storageInfos.put(storageId, current); if (previous != null && !previous.equals(current)) { throw new InvalidSchemaException("incompatible use of storage ID " + storageId + " by both " + previous + " in schema version " + versionMap.get(previous) + " and " + current + " in schema version " + version.versionNumber); } versionMap.put(current, version.versionNumber); } } // Gather all object type storage IDs for (Schema version : this.versions.values()) { version.objTypeMap.values().stream() .map(objType -> objType.storageId) .forEach(objTypeStorageIds::add); } // Gather all reference field storage infos for (StorageInfo info : this.storageInfos.values()) { if (!(info instanceof SimpleFieldStorageInfo)) continue; final SimpleFieldStorageInfo simpleInfo = (SimpleFieldStorageInfo)info; if (!(simpleInfo.fieldType instanceof ReferenceFieldType)) continue; this.referenceFieldIndexStorageInfos.add(simpleInfo); } // Calculate the KeyRanges containing all object types this.objTypesKeyRanges = new KeyRanges(Iterables.transform(this.objTypeStorageIds, ObjId::getKeyRange)); } /** * Verify the given storage ID has the specified type of {@link StorageInfo} and find it. * * @param storageId schema object storage ID * @param expectedType expected {@link StorageInfo} type * @return the actual {@link StorageInfo} instance found * @throws UnknownFieldException if type doesn't match and {@code expectedType} is a {@link FieldStorageInfo} sub-type * @throws UnknownIndexException if type doesn't match and {@code expectedType} is {@link CompositeIndexStorageInfo} * @throws UnknownTypeException if type doesn't match and {@code expectedType} is {@link ObjectStorageInfo} */ T verifyStorageInfo(int storageId, Class expectedType) { final StorageInfo storageInfo = this.storageInfos.get(storageId); if (storageInfo != null && expectedType.isInstance(storageInfo)) return expectedType.cast(storageInfo); String message = "no " + this.getDescription(expectedType) + " with storage ID " + storageId + " exists"; if (storageInfo != null) message += " (found " + storageInfo + " instead)"; if (SimpleFieldStorageInfo.class.isAssignableFrom(expectedType)) throw new UnknownFieldException(storageId, message); if (ObjTypeStorageInfo.class.isAssignableFrom(expectedType)) throw new UnknownTypeException(storageId, 0, message); if (IndexStorageInfo.class.isAssignableFrom(expectedType)) throw new UnknownIndexException(storageId, message); throw new IllegalArgumentException(message); // should never get here } boolean deleteVersion(int version) { final TreeMap newVersions = new TreeMap<>(this.versions); if (newVersions.remove(version) == null) return false; this.initialize(newVersions); return true; } private String getDescription(Class type) { if (SimpleFieldStorageInfo.class.isAssignableFrom(type)) { return type.getSimpleName() .replaceAll("^(.*)StorageInfo$", "$1") .replaceAll("([a-z])([A-Z])", "$1 $2") .toLowerCase() + " index"; } if (ObjTypeStorageInfo.class.isAssignableFrom(type)) return "object type"; if (CompositeIndexStorageInfo.class.isAssignableFrom(type)) return "composite index"; return type.getSimpleName(); // ??? } // Accessors /** * Get all schema versions. * * @return unmodifiable map of {@link Schema}s indexed by version number */ public SortedMap getVersions() { return Collections.unmodifiableSortedMap(this.versions); } /** * Get the {@link Version} corresponding to the given version number. * * @param versionNumber schema version number * @return schema with version number {@code versionNumber} * @throws IllegalArgumentException if {@code versionNumber} is not known */ public Schema getVersion(int versionNumber) { final Schema schema = this.versions.get(versionNumber); if (schema == null) throw new IllegalArgumentException("unknown version " + versionNumber); return schema; } // See if we match encoded schemas read from KV transaction boolean isSameVersions(SortedMap bytesList) { if (bytesList.size() != this.versions.size()) return false; final Iterator> i1 = bytesList.entrySet().iterator(); final Iterator> i2 = this.versions.entrySet().iterator(); while (i1.hasNext() || i2.hasNext()) { if (!i1.hasNext() || !i2.hasNext()) return false; final Map.Entry entry1 = i1.next(); final Map.Entry entry2 = i2.next(); if ((int)entry1.getKey() != (int)entry2.getKey() || !Arrays.equals(entry1.getValue(), entry2.getValue().encodedXML)) return false; } return true; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy