/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ /* WARNING: THIS FILE IS AUTO-GENERATED DO NOT MODIFY THIS SOURCE ALL CHANGES MUST BE MADE IN THE CATALOG GENERATOR */ package org.voltdb.catalog; import java.io.IOException; import java.io.StringReader; import com.google_voltpatches.common.cache.Cache; import com.google_voltpatches.common.cache.CacheBuilder; import com.google_voltpatches.common.io.LineReader; /** * The root class in the Catalog hierarchy, which is essentially a tree of * instances of CatalogType objects, accessed by paths globally, and child * names when given a parent. */ public class Catalog extends CatalogType { public static final char MAP_SEPARATOR = '#'; //private final HashMap<String, CatalogType> m_pathCache = new HashMap<String, CatalogType>(); //private final PatriciaTrie<CatalogType> m_pathCache = new PatriciaTrie<>(); Cache<String, CatalogType> m_pathCache = CacheBuilder.newBuilder().maximumSize(8).build(); private CatalogType m_prevUsedPath = null; CatalogMap<Cluster> m_clusters; /** * Create a new Catalog hierarchy. */ public Catalog() { setBaseValues(null, "catalog"); m_clusters = new CatalogMap<Cluster>(this, this, "clusters", Cluster.class, 1); m_relativeIndex = 1; } @Override void initChildMaps() { // never called on the root catalog object } @Override public Catalog getCatalog() { return this; } @Override public String getCatalogPath() { return "/"; } @Override public void getCatalogPath(StringBuilder sb) { sb.append('/'); } @Override public CatalogType getParent() { return null; } /** * Run one or more single-line catalog commands separated by newlines. * See the docs for more info on catalog statements. * @param commands A string containing one or more catalog commands separated by * newlines */ public void execute(final String commands) { LineReader lines = new LineReader(new StringReader(commands)); int ctr = 0; String line = null; try { while ((line = lines.readLine()) != null) { try { if (line.length() > 0) executeOne(line); } catch (Exception ex) { String msg = "Invalid catalog command on line " + ctr + "\n" + "Contents: '" + line + "'\n"; throw new RuntimeException(msg, ex); } ctr++; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static class CatalogCmd { public char cmd; public String path; public String arg1; public String arg2; public CatalogCmd(char c, String p, String a1, String a2) { cmd = c; path = p; arg1 = a1; arg2 = a2; } public boolean isProcedureRelatedCmd() { if (path.indexOf("procedures#") != -1) return true; if ("procedures".equals(arg1) && path.endsWith("#database")) return true; return false; } } public static char parseStmtCmd(String stmt) { // command comes before the first space (add or set) int pos = 0; while (Character.isWhitespace(stmt.charAt(pos))) { ++pos; } return stmt.charAt(pos++); } public static CatalogCmd parseStmt(String stmt) { // command comes before the first space (add or set) int pos = 0; while (Character.isWhitespace(stmt.charAt(pos))) { ++pos; } char cmd = stmt.charAt(pos++); while (stmt.charAt(pos++) != ' '); // ref to a catalog node between first two spaces int refStart = pos; while (stmt.charAt(pos++) != ' '); String ref = stmt.substring(refStart, pos - 1); // spaces 2 & 3 separate the two arguments int argStart = pos; while (stmt.charAt(pos++) != ' '); String arg1 = stmt.substring(argStart, pos - 1); String arg2 = stmt.substring(pos); return new CatalogCmd(cmd, ref, arg1, arg2); } void executeOne(String stmt) { CatalogCmd catCmd = parseStmt(stmt); char cmd = catCmd.cmd; String path = catCmd.path; String arg1 = catCmd.arg1; String arg2 = catCmd.arg2; // resolve the ref to a node in the catalog CatalogType resolved = null; if (path.startsWith("$")) { // $PREV if (m_prevUsedPath == null) { throw new CatalogException("$PREV reference was not preceded by a cached reference."); } resolved = m_prevUsedPath; } else { resolved = getItemForPath(path); if (resolved == null) { throw new CatalogException("Unable to find reference for catalog item '" + path + "'"); } m_prevUsedPath = resolved; } // run either command if (cmd == 'a') { // add resolved.getCollection(arg1).add(arg2); } else if (cmd == 'd') { // delete resolved.getCollection(arg1).delete(arg2); String toDelete = path + "/" + arg1 + MAP_SEPARATOR + arg2; m_pathCache.invalidate(toDelete); } else if (cmd == 's') { // set resolved.set(arg1, arg2); } } CatalogType getItemForPath(final String path) { // check the cache CatalogType retval = m_pathCache.getIfPresent(path); if (retval != null) return retval; int index = path.lastIndexOf('/'); if (index == -1) { return getItemForPathPart(this, path); } // recursive case String immediateParentPath = path.substring(0, index); String subPath = path.substring(index); CatalogType immediateParent = getItemForPath(immediateParentPath); if (immediateParent == null) { return null; } // cache all parents m_pathCache.put(immediateParentPath, immediateParent); return getItemForPathPart(immediateParent, subPath); } static CatalogType getItemForPathPart(CatalogType parent, String path) { if (path.length() == 0) return parent; boolean hasStartSlash = path.charAt(0) == '/'; if ((path.length() == 1) && hasStartSlash) return parent; int index = path.lastIndexOf(MAP_SEPARATOR); String collection = path.substring(hasStartSlash ? 1 : 0, index); String name = path.substring(index + 1, path.length()); return parent.getCollection(collection).get(name); } /** * Serialize the catalog to a string representation. This actually * creates a set of catalog commands which, re-run in order on an * empty catalog, will recreate this catalog exactly. * @return The serialized string representation of the catalog. */ public String serialize() { StringBuilder sb = new StringBuilder(); writeFieldCommands(sb, null); writeChildCommands(sb); return sb.toString(); } public Catalog deepCopy() { Catalog copy = new Catalog(); // Note that CatalogType.deepCopy isn't called on the catalog node. // need to fully compensate for that here. copy.m_relativeIndex = 1; copy.m_clusters.copyFrom(m_clusters); return copy; } /** GETTER: The set of the clusters in this catalog */ public CatalogMap<Cluster> getClusters() { return m_clusters; } @Override public String[] getFields() { return new String[] {}; } @Override String[] getChildCollections() { return new String[] { "clusters" }; } @Override public Object getField(String field) { switch (field) { case "clusters": return getClusters(); default: throw new CatalogException(String.format("Unknown field: %s in class %s", field, getClass().getSimpleName())); } } @Override void set(String field, String value) { throw new CatalogException("No fields to set in Catalog base object."); } @Override void copyFields(CatalogType obj) { // no fields to copy // also not used as Catalog overrides the calling method of CatalogType } @Override public boolean equals(Object obj) { // this isn't really the convention for null handling if ((obj == null) || (obj.getClass().equals(getClass()) == false)) return false; // Do the identity check if (obj == this) return true; // this is safe because of the class check // it is also known that the childCollections var will be the same // from the class check Catalog other = (Catalog) obj; // are the fields / children the same? (deep compare) if ((m_clusters == null) != (other.m_clusters == null)) return false; if ((m_clusters != null) && !m_clusters.equals(other.m_clusters)) return false; return true; } @Override void writeCreationCommand(StringBuilder sb) { return; } }