/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.github.geophile.erdo.map.diskmap.tree; import com.github.geophile.erdo.AbstractRecord; import com.github.geophile.erdo.RecordFactory; import com.github.geophile.erdo.apiimpl.ConfigurationImpl; import com.github.geophile.erdo.apiimpl.DefaultFactory; import com.github.geophile.erdo.map.Factory; import com.github.geophile.erdo.map.diskmap.DBStructure; import com.github.geophile.erdo.map.diskmap.IndexRecord; import com.github.geophile.erdo.map.diskmap.Manifest; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.util.HashMap; import java.util.Map; public class TreePrinter { public TreePrinter(String[] args) throws Exception { try { int a = 0; File dbDirectory = new File(args[a++]); int treeId = Integer.parseInt(args[a++]); initializeDirectories(dbDirectory, treeId); while (a < args.length) { String arg = args[a++]; if (arg.equals("--keys")) { keysOnly = true; } } } catch (Exception e) { usage(); throw e; } } public void run() throws Exception { readDatabaseProperties(); registerRecordFactories(); computeLeafFileUsage(); printManifest(); printEachLevel(); } private void initializeDirectories(File dbDirectory, int treeId) throws IOException { this.dbStructure = new DBStructure(dbDirectory); this.treeId = treeId; if (!dbStructure.manifestFile(treeId).exists()) { throw new IOException(String.format("%s does not exist", dbStructure.manifestFile(treeId))); } if (!dbStructure.forestDirectory().exists()) { throw new IOException(String.format("%s does not exist", dbStructure.forestDirectory())); } if (!dbStructure.segmentsDirectory().exists()) { throw new IOException(String.format("%s does not exist", dbStructure.segmentsDirectory())); } if (!dbStructure.summariesDirectory().exists()) { throw new IOException(String.format("%s does not exist", dbStructure.summariesDirectory())); } } private void readDatabaseProperties() throws IOException { ConfigurationImpl configuration = new ConfigurationImpl(); configuration.read(dbStructure.dbPropertiesFile()); factory = new DefaultFactory(configuration); } private void registerRecordFactories() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException { for (String mapFileName : dbStructure.mapsDirectory().list()) { int erdoId = Integer.parseInt(mapFileName); ObjectInputStream input = new ObjectInputStream(new FileInputStream(new File(dbStructure.mapsDirectory(), mapFileName))); /* String mapName = (String) */ input.readObject(); RecordFactory recordFactory = (RecordFactory) input.readObject(); input.close(); factory.registerRecordFactory(erdoId, recordFactory); } } private void computeLeafFileUsage() throws IOException { // Read all manifests and create a map counting usage of each leaf file File[] files = dbStructure.forestDirectory().listFiles(); assert files != null : dbStructure.forestDirectory(); for (File manifestFile : files) { Manifest manifest = Manifest.read(manifestFile); if (manifest.levels() > 0) { for (long segmentId : manifest.segmentIds(0)) { Integer count = leafSegmentCounts.get(segmentId); if (count == null) { count = 1; } else { count = count + 1; } leafSegmentCounts.put(segmentId, count); } } } } private void printManifest() { try { manifest = Manifest.read(dbStructure.manifestFile(treeId)); System.out.println(String.format("treeId: %s", manifest.treeId())); System.out.println(String.format("levels: %s", manifest.levels())); System.out.println(String.format("recordCount: %s", manifest.recordCount())); System.out.println(String.format("timestamps: %s", manifest.timestamps())); System.out.println(); } catch (IOException e) { System.out.println(e.getMessage()); } } private void printEachLevel() throws IOException, InterruptedException { Tree tree = Tree.recover(factory, dbStructure, manifest); for (int level = tree.levels() - 1; level >= 0; level--) { TreeLevel treeLevel = tree.level(level); if (level == 0) { printLeafLevel(treeLevel); } else { printNonLeafLevel(treeLevel); } } } private void printNonLeafLevel(TreeLevel level) throws IOException, InterruptedException { Tree tree = level.tree(); TreePosition position = tree.newPosition().level(level.levelNumber()) .goToFirstSegmentOfLevel() .goToFirstPageOfSegment() .goToFirstRecordOfPage(); print(0, "%s", level); TreeSegment lastSegment = null; int lastPageAddress = -1; while (!position.atEnd()) { if (position.segment() != lastSegment) { print(1, "%s", position.segment()); lastSegment = position.segment(); } if (position.pageAddress() != lastPageAddress) { int pageAddress = position.pageAddress(); print(2, "S%s/P%s", tree.segmentNumber(pageAddress), tree.pageNumber(pageAddress)); lastPageAddress = pageAddress; } IndexRecord indexRecord = (IndexRecord) position.materializeRecord(); int childAddress = indexRecord.childPageAddress(); print(3, "%s: S%s/P%s", indexRecord.key(), tree.segmentNumber(childAddress), tree.pageNumber(childAddress)); if (level.levelNumber() == 1) { int leafSegmentNumber = tree.segmentNumber(childAddress); int leafPageNumber = tree.pageNumber(childAddress); TreeSegment leafSegment = tree.level(0).segment(leafSegmentNumber); if (leafPageNumber == leafSegment.pages() - 1) { print(3, "%s", leafSegment.leafLastKey()); } } position.goToNextRecord(); } } private void printLeafLevel(TreeLevel level) throws IOException, InterruptedException { Tree tree = level.tree(); TreePosition position = tree.newPosition().level(0) .goToFirstSegmentOfLevel() .goToFirstPageOfSegment() .goToFirstRecordOfPage(); print(0, "%s", level); TreeSegment lastSegment = null; int lastPageAddress = -1; while (!position.atEnd()) { if (position.segment() != lastSegment) { int links = links(position); if (links == 1) { print(1, "%s", position.segment()); } else { print(1, "%s (%s links)", position.segment(), links); } lastSegment = position.segment(); } if (position.pageAddress() != lastPageAddress) { int pageAddress = position.pageAddress(); print(2, "S%s/P%s", tree.segmentNumber(pageAddress), tree.pageNumber(pageAddress)); lastPageAddress = pageAddress; } AbstractRecord record = position.materializeRecord(); if (keysOnly) { print(3, "%s", record.key()); } else { print(3, "%s", record); } position.goToNextRecord(); } } private void print(int indent, String template, Object... args) { for (int i = 0; i < indent; i++) { System.out.print(" "); } System.out.println(String.format(template, args)); } private int links(TreePosition position) { return leafSegmentCounts.get(position.segment().segmentId()); } private void usage() { for (String s : USAGE) { System.err.println(s); } System.exit(1); } private static final String[] USAGE = { "treeprint DB_DIRECTORY TREE_ID [--keys]", " DB_DIRECTORY: Root directory of the database", " TREE_ID: Tree id", " --factory: Used to specify record factory class for an erdo id", " --keys: If specified, only keys are printed", "If the database directory containing TREE_DIR has an intact maps directory, then the erdo ids and", "record factory classnames will be read from it. ERDO_IDs and RECORD_FACTORY_CLASSNAMEs need only be", "provided if the maps directory is unusable." }; private Manifest manifest; private DBStructure dbStructure; private int treeId; private Factory factory; private final Map<Long, Integer> leafSegmentCounts = new HashMap<>(); private boolean keysOnly = false; }