You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by fr...@apache.org on 2016/05/23 16:35:43 UTC

svn commit: r1745235 - in /jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak: explorer/Explorer.java explorer/FileStoreWrapper.java explorer/NodeStoreTree.java run/SegmentUtils.java

Author: frm
Date: Mon May 23 16:35:43 2016
New Revision: 1745235

URL: http://svn.apache.org/viewvc?rev=1745235&view=rev
Log:
OAK-4337 - Encapsulate every operation related to the FileStore in the same class

Added:
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/FileStoreWrapper.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/NodeStoreTree.java
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java

Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java?rev=1745235&r1=1745234&r2=1745235&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/Explorer.java Mon May 23 16:35:43 2016
@@ -18,11 +18,7 @@
  */
 package org.apache.jackrabbit.oak.explorer;
 
-import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.readRevisions;
-
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.Insets;
+import java.awt.*;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.KeyEvent;
@@ -31,16 +27,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
-import javax.swing.JFrame;
-import javax.swing.JMenuBar;
-import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JSeparator;
-import javax.swing.JSplitPane;
-import javax.swing.JTextArea;
-import javax.swing.UIManager;
+import javax.swing.*;
 import javax.swing.UIManager.LookAndFeelInfo;
 
 import org.apache.commons.io.IOUtils;
@@ -53,21 +40,40 @@ import org.apache.commons.io.IOUtils;
  */
 public class Explorer {
 
-    private static String skip = "skip-size-check";
+    private static boolean getSkipSizeChecks(String... args) {
+        return args.length >= 2 && args[1].equalsIgnoreCase("skip-size-check");
+    }
+
+    private static void initLF() {
+        try {
+            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
+                if ("Nimbus".equals(info.getName())) {
+                    UIManager.setLookAndFeel(info.getClassName());
+                    break;
+                }
+            }
+        } catch (Exception e) {
+            // If Nimbus is not available, you can set the GUI to another look
+            // and feel.
+        }
+    }
 
     public static void main(String[] args) throws IOException {
         new Explorer(args);
     }
 
-    public Explorer(String[] args) throws IOException {
+    private final FileStoreWrapper store;
+
+    private Explorer(String[] args) throws IOException {
         if (args.length == 0) {
             System.err.println("usage: explore <path> [skip-size-check]");
             System.exit(1);
         }
 
         final File path = new File(args[0]);
-        final boolean skipSizeCheck = args.length == 2
-                && skip.equalsIgnoreCase(args[1]);
+        final boolean skipSizeCheck = getSkipSizeChecks(args);
+
+        this.store = new FileStoreWrapper(path);
 
         javax.swing.SwingUtilities.invokeLater(new Runnable() {
             public void run() {
@@ -81,29 +87,13 @@ public class Explorer {
         });
     }
 
-    private static void initLF() {
-        try {
-            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
-                if ("Nimbus".equals(info.getName())) {
-                    UIManager.setLookAndFeel(info.getClassName());
-                    break;
-                }
-            }
-        } catch (Exception e) {
-            // If Nimbus is not available, you can set the GUI to another look
-            // and feel.
-        }
-    }
-
-    private void createAndShowGUI(final File path, boolean skipSizeCheck)
-            throws IOException {
-
+    private void createAndShowGUI(final File path, boolean skipSizeCheck) throws IOException {
         JTextArea log = new JTextArea(5, 20);
         log.setMargin(new Insets(5, 5, 5, 5));
         log.setLineWrap(true);
         log.setEditable(false);
 
-        final NodeStoreTree treePanel = new NodeStoreTree(path, log, skipSizeCheck);
+        final NodeStoreTree treePanel = new NodeStoreTree(store, log, skipSizeCheck);
 
         final JFrame frame = new JFrame("Explore " + path + " @head");
         frame.addWindowListener(new java.awt.event.WindowAdapter() {
@@ -148,7 +138,7 @@ public class Explorer {
         menuCompaction.addActionListener(new ActionListener() {
             @Override
             public void actionPerformed(ActionEvent ev) {
-                List<String> revs = readRevisions(path);
+                List<String> revs = store.readRevisions();
                 String s = (String) JOptionPane.showInputDialog(frame,
                         "Revert to a specified revision", "Time Machine",
                         JOptionPane.PLAIN_MESSAGE, null, revs.toArray(),
@@ -177,7 +167,6 @@ public class Explorer {
                         tarFiles.get(0));
                 if (s != null) {
                     treePanel.printTarInfo(s);
-                    return;
                 }
             }
         });
@@ -187,12 +176,11 @@ public class Explorer {
         menuSCR.addActionListener(new ActionListener() {
             @Override
             public void actionPerformed(ActionEvent ev) {
-                String s = (String) JOptionPane.showInputDialog(frame,
+                String s = JOptionPane.showInputDialog(frame,
                         "Segment References\nUsage: <segmentId>",
                         "Segment References", JOptionPane.PLAIN_MESSAGE);
                 if (s != null) {
                     treePanel.printSegmentReferences(s);
-                    return;
                 }
             }
         });
@@ -202,12 +190,11 @@ public class Explorer {
         menuDiff.addActionListener(new ActionListener() {
             @Override
             public void actionPerformed(ActionEvent ev) {
-                String s = (String) JOptionPane.showInputDialog(frame,
+                String s = JOptionPane.showInputDialog(frame,
                         "SegmentNodeState diff\nUsage: <recordId> <recordId> [<path>]",
                         "SegmentNodeState diff", JOptionPane.PLAIN_MESSAGE);
                 if (s != null) {
                     treePanel.printDiff(s);
-                    return;
                 }
             }
         });

Added: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/FileStoreWrapper.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/FileStoreWrapper.java?rev=1745235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/FileStoreWrapper.java (added)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/FileStoreWrapper.java Mon May 23 16:35:43 2016
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.explorer;
+
+import static com.google.common.collect.Sets.newHashSet;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.UUID;
+
+import com.google.common.collect.Maps;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper;
+import org.apache.jackrabbit.oak.plugins.segment.PCMAnalyser;
+import org.apache.jackrabbit.oak.plugins.segment.RecordId;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentBlob;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentId;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeState;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStateHelper;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentPropertyState;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+class FileStoreWrapper {
+
+    private final File path;
+
+    private FileStore.ReadOnlyStore store;
+
+    private Map<String, Set<UUID>> index;
+
+    FileStoreWrapper(File path) throws IOException {
+        this.path = path;
+    }
+
+    void open() throws IOException {
+        store = FileStore.builder(path).buildReadOnly();
+        index = store.getTarReaderIndex();
+    }
+
+    void close() {
+        store.close();
+        store = null;
+        index = null;
+    }
+
+    List<String> readRevisions() {
+        return FileStoreHelper.readRevisions(path);
+    }
+
+    Map<String, Set<UUID>> getTarReaderIndex() {
+        return store.getTarReaderIndex();
+    }
+
+    Map<UUID, List<UUID>> getTarGraph(String file) throws IOException {
+        return store.getTarGraph(file);
+    }
+
+    List<String> getTarFiles() {
+        return FileStoreHelper.getTarFiles(store);
+    }
+
+    void getGcRoots(UUID uuidIn, Map<UUID, Set<Entry<UUID, String>>> links) throws IOException {
+        FileStoreHelper.getGcRoots(store, uuidIn, links);
+    }
+
+    Set<UUID> getReferencedSegmentIds() {
+        Set<UUID> ids = newHashSet();
+
+        for (SegmentId id : store.getTracker().getReferencedSegmentIds()) {
+            ids.add(id.asUUID());
+        }
+
+        return ids;
+    }
+
+    NodeState getHead() {
+        return store.getHead();
+    }
+
+    NodeState readNodeState(String recordId) {
+        return new SegmentNodeState(RecordId.fromString(store.getTracker(), recordId));
+    }
+
+    void setRevision(String revision) {
+        store.setRevision(revision);
+    }
+
+    boolean isPersisted(NodeState state) {
+        return state instanceof SegmentNodeState;
+    }
+
+    boolean isPersisted(PropertyState state) {
+        return state instanceof SegmentPropertyState;
+    }
+
+    String getRecordId(NodeState state) {
+        if (state instanceof SegmentNodeState) {
+            return getRecordId((SegmentNodeState) state);
+        }
+
+        return null;
+    }
+
+    UUID getSegmentId(NodeState state) {
+        if (state instanceof SegmentNodeState) {
+            return getSegmentId((SegmentNodeState) state);
+        }
+
+        return null;
+    }
+
+    String getRecordId(PropertyState state) {
+        if (state instanceof SegmentPropertyState) {
+            return getRecordId((SegmentPropertyState) state);
+        }
+
+        return null;
+    }
+
+    UUID getSegmentId(PropertyState state) {
+        if (state instanceof SegmentPropertyState) {
+            return getSegmentId((SegmentPropertyState) state);
+        }
+
+        return null;
+    }
+
+    String getTemplateRecordId(NodeState state) {
+        if (state instanceof SegmentNodeState) {
+            return getTemplateRecordId((SegmentNodeState) state);
+        }
+
+        return null;
+    }
+
+    UUID getTemplateSegmentId(NodeState state) {
+        if (state instanceof SegmentNodeState) {
+            return getTemplateSegmentId((SegmentNodeState) state);
+        }
+
+        return null;
+    }
+
+    String getFile(NodeState state) {
+        if (state instanceof SegmentNodeState) {
+            return getFile((SegmentNodeState) state);
+        }
+
+        return null;
+    }
+
+    String getFile(PropertyState state) {
+        if (state instanceof SegmentPropertyState) {
+            return getFile((SegmentPropertyState) state);
+        }
+
+        return null;
+    }
+
+    String getTemplateFile(NodeState state) {
+        if (state instanceof SegmentNodeState) {
+            return getTemplateFile((SegmentNodeState) state);
+        }
+
+        return null;
+    }
+
+    Map<UUID, String> getBulkSegmentIds(Blob blob) {
+        Map<UUID, String> result = Maps.newHashMap();
+
+        for (SegmentId segmentId : SegmentBlob.getBulkSegmentIds(blob)) {
+            result.put(segmentId.asUUID(), getFile(segmentId));
+        }
+
+        return result;
+    }
+
+    String getPersistedCompactionMapStats() {
+        return new PCMAnalyser(store).toString();
+    }
+
+    boolean isExternal(Blob blob) {
+        if (blob instanceof SegmentBlob) {
+            return isExternal((SegmentBlob) blob);
+        }
+
+        return false;
+    }
+
+    private boolean isExternal(SegmentBlob blob) {
+        return blob.isExternal();
+    }
+
+    private String getRecordId(SegmentNodeState state) {
+        return state.getRecordId().toString();
+    }
+
+    private UUID getSegmentId(SegmentNodeState state) {
+        return state.getRecordId().getSegmentId().asUUID();
+    }
+
+    private String getRecordId(SegmentPropertyState state) {
+        return state.getRecordId().toString();
+    }
+
+    private UUID getSegmentId(SegmentPropertyState state) {
+        return state.getRecordId().getSegmentId().asUUID();
+    }
+
+    private String getTemplateRecordId(SegmentNodeState state) {
+        RecordId recordId = SegmentNodeStateHelper.getTemplateId(state);
+
+        if (recordId == null) {
+            return null;
+        }
+
+        return recordId.toString();
+    }
+
+    private UUID getTemplateSegmentId(SegmentNodeState state) {
+        RecordId recordId = SegmentNodeStateHelper.getTemplateId(state);
+
+        if (recordId == null) {
+            return null;
+        }
+
+        return recordId.getSegmentId().asUUID();
+    }
+
+    private String getFile(SegmentNodeState state) {
+        return getFile(state.getRecordId().getSegmentId());
+    }
+
+    private String getFile(SegmentPropertyState state) {
+        return getFile(state.getRecordId().getSegmentId());
+    }
+
+    private String getTemplateFile(SegmentNodeState state) {
+        RecordId recordId = SegmentNodeStateHelper.getTemplateId(state);
+
+        if (recordId == null) {
+            return null;
+        }
+
+        return getFile(recordId.getSegmentId());
+    }
+
+    private String getFile(SegmentId segmentId) {
+        for (Entry<String, Set<UUID>> path2Uuid : index.entrySet()) {
+            for (UUID uuid : path2Uuid.getValue()) {
+                if (uuid.equals(segmentId.asUUID())) {
+                    return new File(path2Uuid.getKey()).getName();
+                }
+            }
+        }
+        return null;
+    }
+
+}

Propchange: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/FileStoreWrapper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/NodeStoreTree.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/NodeStoreTree.java?rev=1745235&r1=1745234&r2=1745235&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/NodeStoreTree.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/explorer/NodeStoreTree.java Mon May 23 16:35:43 2016
@@ -34,27 +34,18 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
 import static org.apache.jackrabbit.oak.commons.json.JsopBuilder.prettyPrint;
 import static org.apache.jackrabbit.oak.json.JsopDiff.diffToJsop;
-import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.getGcRoots;
-import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.getTarFiles;
-import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.newline;
-import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.printGcRoots;
-import static org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStateHelper.getTemplateId;
 
-import java.awt.GridLayout;
+import java.awt.*;
 import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.UUID;
 
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-import javax.swing.JTree;
+import javax.swing.*;
 import javax.swing.event.TreeSelectionEvent;
 import javax.swing.event.TreeSelectionListener;
 import javax.swing.tree.DefaultMutableTreeNode;
@@ -63,41 +54,91 @@ import javax.swing.tree.DefaultTreeModel
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.plugins.segment.PCMAnalyser;
-import org.apache.jackrabbit.oak.plugins.segment.RecordId;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentBlob;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentId;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeState;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentNotFoundException;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentPropertyState;
-import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
-import org.apache.jackrabbit.oak.plugins.segment.file.FileStore.ReadOnlyStore;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
-public class NodeStoreTree extends JPanel implements TreeSelectionListener,
-        Closeable {
+public class NodeStoreTree extends JPanel implements TreeSelectionListener, Closeable {
 
     private static final long serialVersionUID = 1L;
 
-    private final static int MAX_CHAR_DISPLAY = Integer.getInteger(
-            "max.char.display", 60);
+    private final static int MAX_CHAR_DISPLAY = Integer.getInteger("max.char.display", 60);
+
+    private static final String newline = "\n";
+
+    private static void printGcRoots(StringBuilder sb, Map<UUID, Set<Entry<UUID, String>>> links, UUID uuid, String space, String inc) {
+        Set<Entry<UUID, String>> roots = links.remove(uuid);
+
+        if (roots == null || roots.isEmpty()) {
+            return;
+        }
+
+        // TODO is sorting by file name needed?
+        for (Entry<UUID, String> r : roots) {
+            sb.append(space).append(r.getKey()).append("[").append(r.getValue()).append("]").append(newline);
+            printGcRoots(sb, links, r.getKey(), space + inc, inc);
+        }
+    }
+
+    private static void printPaths(List<String> paths, StringBuilder sb) {
+        if (paths.isEmpty()) {
+            return;
+        }
+
+        sb.append("Repository content references:").append(newline);
+
+        for (String p : paths) {
+            sb.append(p).append(newline);
+        }
+    }
+
+    private static void printPropertyState(FileStoreWrapper store, PropertyState state, String parentFile, StringBuilder b) {
+        if (store.isPersisted(state)) {
+            printRecordId(store.getRecordId(state), store.getFile(state), parentFile, b);
+        } else {
+            printSimpleClassName(state, b);
+        }
+    }
+
+    private static void printNodeState(FileStoreWrapper store, NodeState state, String parentTarFile, StringBuilder b) {
+        if (store.isPersisted(state)) {
+            printRecordId(store.getRecordId(state), store.getFile(state), parentTarFile, b);
+        } else {
+            printSimpleClassName(state, b);
+        }
+    }
+
+    private static void printRecordId(String recordId, String file, String parentFile, StringBuilder l) {
+        l.append(" (").append(recordId);
+
+        if (file != null && !file.equals(parentFile)) {
+            l.append(" in ").append(file);
+        }
+
+        l.append(")");
+    }
+
+    private static void printSimpleClassName(Object o, StringBuilder l) {
+        l.append(" (").append(o.getClass().getSimpleName()).append(")");
+    }
+
+    private final FileStoreWrapper store;
 
-    private final File path;
-    private ReadOnlyStore store;
     private Map<String, Set<UUID>> index;
 
     private DefaultTreeModel treeModel;
+
     private final JTree tree;
+
     private final JTextArea log;
 
-    private Map<RecordIdKey, Long[]> sizeCache;
+    private Map<String, Long[]> sizeCache;
+
     private final boolean skipSizeCheck;
 
-    public NodeStoreTree(File path, JTextArea log, boolean skipSizeCheck)
+    NodeStoreTree(FileStoreWrapper store, JTextArea log, boolean skipSizeCheck)
             throws IOException {
         super(new GridLayout(1, 0));
-        this.path = path;
+        this.store = store;
         this.log = log;
         this.skipSizeCheck = skipSizeCheck;
 
@@ -115,7 +156,7 @@ public class NodeStoreTree extends JPane
     }
 
     private void refreshStore() throws IOException {
-        this.store = FileStore.builder(path).buildReadOnly();
+        store.open();
     }
 
     private void refreshModel() {
@@ -129,7 +170,7 @@ public class NodeStoreTree extends JPane
         tree.setModel(treeModel);
     }
 
-    public void reopen() throws IOException {
+    void reopen() throws IOException {
         close();
         refreshStore();
         refreshModel();
@@ -155,10 +196,10 @@ public class NodeStoreTree extends JPane
 
             NamePathModel model = (NamePathModel) node.getUserObject();
             NodeState state = model.getState();
-            if (state instanceof SegmentNodeState) {
-                SegmentNodeState sns = (SegmentNodeState) state;
+            String recordId = store.getRecordId(state);
+            if (recordId != null) {
                 sb.append("Record ");
-                sb.append(sns.getRecordId().toString());
+                sb.append(recordId);
                 sb.append(newline);
             }
             setText(sb.toString());
@@ -202,22 +243,21 @@ public class NodeStoreTree extends JPane
         NodeState state = model.getState();
         String tarFile = "";
 
-        if (state instanceof SegmentNodeState) {
-            SegmentNodeState s = (SegmentNodeState) state;
-            RecordId recordId = s.getRecordId();
-            sb.append("Record " + recordId);
-            tarFile = getFile(recordId);
-            if (tarFile.length() > 0) {
-                sb.append(" in " + tarFile);
+        if (store.isPersisted(state)) {
+            String recordId = store.getRecordId(state);
+            sb.append("Record ").append(recordId);
+            tarFile = store.getFile(state);
+            if (tarFile != null) {
+                sb.append(" in ").append(tarFile);
             }
             sb.append(newline);
 
-            RecordId templateId = getTemplateId(s);
-            String f = getFile(templateId);
+            String templateId = store.getTemplateRecordId(state);
+            String f = store.getTemplateFile(state);
             sb.append("TemplateId ");
             sb.append(templateId);
-            if (!f.equals(tarFile)) {
-                sb.append(" in " + f);
+            if (f != null && !f.equals(tarFile)) {
+                sb.append(" in ").append(f);
             }
             sb.append(newline);
         }
@@ -229,15 +269,15 @@ public class NodeStoreTree extends JPane
         sb.append(byteCountToDisplaySize(model.getSize()[1]));
         sb.append(newline);
 
-        sb.append("Properties (count: " + state.getPropertyCount() + ")");
+        sb.append("Properties (count: ").append(state.getPropertyCount()).append(")");
         sb.append(newline);
         Map<String, String> propLines = newTreeMap();
         for (PropertyState ps : state.getProperties()) {
             StringBuilder l = new StringBuilder();
-            l.append("  - " + ps.getName() + " = {" + ps.getType() + "} ");
+            l.append("  - ").append(ps.getName()).append(" = {").append(ps.getType()).append("} ");
             if (ps.getType().isArray()) {
                 int count = ps.count();
-                l.append("(count " + count + ") [");
+                l.append("(count ").append(count).append(") [");
 
                 String separator = ", ";
                 int max = 50;
@@ -253,7 +293,7 @@ public class NodeStoreTree extends JPane
                     l.append(toString(ps, i, tarFile));
                 }
                 if (count > max) {
-                    l.append(", ... (" + count + " values)");
+                    l.append(", ... (").append(count).append(" values)");
                 }
                 if (ps.getType() == Type.BINARIES) {
                     l.append(separator);
@@ -263,17 +303,7 @@ public class NodeStoreTree extends JPane
             } else {
                 l.append(toString(ps, 0, tarFile));
             }
-            if (ps instanceof SegmentPropertyState) {
-                RecordId rid = ((SegmentPropertyState) ps).getRecordId();
-                l.append(" (" + rid);
-                String f = getFile(rid);
-                if (!f.equals(tarFile)) {
-                    l.append(" in " + f);
-                }
-                l.append(")");
-            } else {
-                l.append(" (" + ps.getClass().getSimpleName() + ")");
-            }
+            printPropertyState(store, ps, tarFile, l);
             propLines.put(ps.getName(), l.toString());
         }
 
@@ -282,25 +312,14 @@ public class NodeStoreTree extends JPane
             sb.append(newline);
         }
 
-        sb.append("Child nodes (count: "
-                + state.getChildNodeCount(Long.MAX_VALUE) + ")");
+        sb.append("Child nodes (count: ").append(state.getChildNodeCount(Long.MAX_VALUE)).append(")");
         sb.append(newline);
         Map<String, String> childLines = newTreeMap();
         for (ChildNodeEntry ce : state.getChildNodeEntries()) {
             StringBuilder l = new StringBuilder();
-            l.append("  + " + ce.getName());
+            l.append("  + ").append(ce.getName());
             NodeState c = ce.getNodeState();
-            if (c instanceof SegmentNodeState) {
-                RecordId rid = ((SegmentNodeState) c).getRecordId();
-                l.append(" (" + rid);
-                String f = getFile(rid);
-                if (!f.equals(tarFile)) {
-                    l.append(" in " + f);
-                }
-                l.append(")");
-            } else {
-                l.append(" (" + c.getClass().getSimpleName() + ")");
-            }
+            printNodeState(store, c, tarFile, l);
             childLines.put(ce.getName(), l.toString());
         }
         for (String l : childLines.values()) {
@@ -312,7 +331,7 @@ public class NodeStoreTree extends JPane
             sb.append("File Reader Index");
             sb.append(newline);
 
-            for (String path : getTarFiles(store)) {
+            for (String path : store.getTarFiles()) {
                 sb.append(path);
                 sb.append(newline);
             }
@@ -330,11 +349,11 @@ public class NodeStoreTree extends JPane
             info += "ref:" + safeGetReference(b) + ";";
             info += "id:" + b.getContentIdentity() + ";";
             info += safeGetLength(b) + ">";
-            for (SegmentId sid : SegmentBlob.getBulkSegmentIds(b)) {
-                info += newline + "        Bulk Segment Id " + sid;
-                String f = getFile(sid);
-                if (!f.equals(tarFile)) {
-                    info += " in " + f;
+
+            for (Entry<UUID, String> e : store.getBulkSegmentIds(b).entrySet()) {
+                info += newline + "        Bulk Segment Id " + e.getKey();
+                if (e.getValue() != null && !e.getValue().equals(tarFile)) {
+                    info += " in " + e.getValue();
                 }
             }
 
@@ -375,22 +394,7 @@ public class NodeStoreTree extends JPane
         return "[BlobStore not available]";
     }
 
-    private String getFile(RecordId id) {
-        return getFile(id.getSegmentId());
-    }
-
-    private String getFile(SegmentId segmentId) {
-        for (Entry<String, Set<UUID>> path2Uuid : index.entrySet()) {
-            for (UUID uuid : path2Uuid.getValue()) {
-                if (uuid.equals(segmentId.asUUID())) {
-                    return new File(path2Uuid.getKey()).getName();
-                }
-            }
-        }
-        return "";
-    }
-
-    public void printTarInfo(String file) {
+    void printTarInfo(String file) {
         if (file == null || file.length() == 0) {
             return;
         }
@@ -399,15 +403,14 @@ public class NodeStoreTree extends JPane
         Set<UUID> uuids = newHashSet();
         for (Entry<String, Set<UUID>> e : index.entrySet()) {
             if (e.getKey().endsWith(file)) {
-                sb.append("SegmentNodeState references to "
-                        + new File(e.getKey()).getName());
+                sb.append("SegmentNodeState references to ").append(new File(e.getKey()).getName());
                 sb.append(newline);
                 uuids = e.getValue();
                 break;
             }
         }
 
-        Set<UUID> inMem = intersection(getReferencedUUIDs(store), uuids);
+        Set<UUID> inMem = intersection(store.getReferencedSegmentIds(), uuids);
         if (!inMem.isEmpty()) {
             sb.append("In Memory segment references: ");
             sb.append(newline);
@@ -416,15 +419,8 @@ public class NodeStoreTree extends JPane
         }
 
         List<String> paths = newArrayList();
-        filterNodeStates(uuids, paths, store.getHead(), "/");
-        if (!paths.isEmpty()) {
-            sb.append("Repository content references:");
-            sb.append(newline);
-            for (String p : paths) {
-                sb.append(p);
-                sb.append(newline);
-            }
-        }
+        filterNodeStates(uuids, paths, store.getHead(), "/", store);
+        printPaths(paths, sb);
 
         sb.append(newline);
         try {
@@ -442,15 +438,7 @@ public class NodeStoreTree extends JPane
         setText(sb.toString());
     }
 
-    private static Set<UUID> getReferencedUUIDs(FileStore store) {
-        Set<UUID> ids = newHashSet();
-        for (SegmentId id : store.getTracker().getReferencedSegmentIds()) {
-            ids.add(id.asUUID());
-        }
-        return ids;
-    }
-
-    public void printSegmentReferences(String sid) {
+    void printSegmentReferences(String sid) {
         if (sid == null || sid.length() == 0) {
             return;
         }
@@ -462,30 +450,23 @@ public class NodeStoreTree extends JPane
             return;
         }
         StringBuilder sb = new StringBuilder();
-        sb.append("References to segment " + id);
+        sb.append("References to segment ").append(id);
         sb.append(newline);
         for (Entry<String, Set<UUID>> e : index.entrySet()) {
             if (e.getValue().contains(id)) {
-                sb.append("Tar file: " + new File(e.getKey()).getName());
+                sb.append("Tar file: ").append(new File(e.getKey()).getName());
                 sb.append(newline);
                 break;
             }
         }
 
         List<String> paths = newArrayList();
-        filterNodeStates(newHashSet(id), paths, store.getHead(), "/");
-        if (!paths.isEmpty()) {
-            sb.append("Repository content references:");
-            sb.append(newline);
-            for (String p : paths) {
-                sb.append(p);
-                sb.append(newline);
-            }
-        }
+        filterNodeStates(newHashSet(id), paths, store.getHead(), "/", store);
+        printPaths(paths, sb);
 
         Map<UUID, Set<Entry<UUID, String>>> links = newHashMap();
         try {
-            getGcRoots(store, id, links);
+            store.getGcRoots(id, links);
         } catch (IOException e) {
             sb.append(newline);
             sb.append(e.getMessage());
@@ -499,14 +480,12 @@ public class NodeStoreTree extends JPane
         setText(sb.toString());
     }
 
-    public static void filterNodeStates(Set<UUID> uuids, List<String> paths,
-            SegmentNodeState state, String path) {
+    private static void filterNodeStates(Set<UUID> uuids, List<String> paths, NodeState state, String path, FileStoreWrapper store) {
         Set<String> localPaths = newTreeSet();
         for (PropertyState ps : state.getProperties()) {
-            if (ps instanceof SegmentPropertyState) {
-                SegmentPropertyState sps = (SegmentPropertyState) ps;
-                RecordId recordId = sps.getRecordId();
-                UUID id = recordId.getSegmentId().asUUID();
+            if (store.isPersisted(ps)) {
+                String recordId = store.getRecordId(ps);
+                UUID id = store.getSegmentId(ps);
                 if (uuids.contains(id)) {
                     if (ps.getType().tag() == STRING) {
                         String val = "";
@@ -527,9 +506,8 @@ public class NodeStoreTree extends JPane
                     // look for extra segment references
                     for (int i = 0; i < ps.count(); i++) {
                         Blob b = ps.getValue(Type.BINARY, i);
-                        for (SegmentId sbid : SegmentBlob.getBulkSegmentIds(b)) {
-                            UUID bid = sbid.asUUID();
-                            if (!bid.equals(id) && uuids.contains(bid)) {
+                        for (Entry<UUID, String> e : store.getBulkSegmentIds(b).entrySet()) {
+                            if (!e.getKey().equals(id) && uuids.contains(e.getKey())) {
                                 localPaths.add(path + ps
                                         + " [SegmentPropertyState<"
                                         + ps.getType() + ">@" + recordId + "]");
@@ -540,48 +518,38 @@ public class NodeStoreTree extends JPane
             }
         }
 
-        RecordId stateId = state.getRecordId();
-        if (uuids.contains(stateId.getSegmentId().asUUID())) {
+        String stateId = store.getRecordId(state);
+        if (uuids.contains(store.getSegmentId(state))) {
             localPaths.add(path + " [SegmentNodeState@" + stateId + "]");
         }
 
-        RecordId templateId = getTemplateId(state);
-        if (uuids.contains(templateId.getSegmentId().asUUID())) {
+        String templateId = store.getTemplateRecordId(state);
+        if (uuids.contains(store.getTemplateSegmentId(state))) {
             localPaths.add(path + "[Template@" + templateId + "]");
         }
         paths.addAll(localPaths);
         for (ChildNodeEntry ce : state.getChildNodeEntries()) {
-            NodeState c = ce.getNodeState();
-            if (c instanceof SegmentNodeState) {
-                filterNodeStates(uuids, paths, (SegmentNodeState) c,
-                        path + ce.getName() + "/");
-            }
+            filterNodeStates(uuids, paths, ce.getNodeState(), path + ce.getName() + "/", store);
         }
     }
 
-    public void printDiff(String input) {
+    void printDiff(String input) {
         StringBuilder sb = new StringBuilder();
         if (input == null || input.trim().length() == 0) {
-            sb.append("Unknown argument: ");
-            sb.append(input);
-            sb.append(newline);
             setText("Usage <recordId> <recordId> [<path>]");
             return;
         }
 
         String[] tokens = input.trim().split(" ");
         if (tokens.length != 2 && tokens.length != 3) {
-            sb.append("Unknown argument: ");
-            sb.append(input);
-            sb.append(newline);
             setText("Usage <recordId> <recordId> [<path>]");
             return;
         }
-        RecordId id1 = null;
-        RecordId id2 = null;
+        NodeState node1;
+        NodeState node2;
         try {
-            id1 = RecordId.fromString(store.getTracker(), tokens[0]);
-            id2 = RecordId.fromString(store.getTracker(), tokens[1]);
+            node1 = store.readNodeState(tokens[0]);
+            node2 = store.readNodeState(tokens[1]);
         } catch (IllegalArgumentException ex) {
             sb.append("Unknown argument: ");
             sb.append(input);
@@ -597,17 +565,15 @@ public class NodeStoreTree extends JPane
             path = tokens[2];
         }
 
-        NodeState node1 = new SegmentNodeState(id1);
-        NodeState node2 = new SegmentNodeState(id2);
         for (String name : elements(path)) {
             node1 = node1.getChildNode(name);
             node2 = node2.getChildNode(name);
         }
 
         sb.append("SegmentNodeState diff ");
-        sb.append(id1);
+        sb.append(tokens[0]);
         sb.append(" vs ");
-        sb.append(id2);
+        sb.append(tokens[1]);
         sb.append(" at ");
         sb.append(path);
         sb.append(newline);
@@ -617,19 +583,19 @@ public class NodeStoreTree extends JPane
         setText(sb.toString());
     }
 
-    public boolean revert(String revision) {
+    boolean revert(String revision) {
         return safeRevert(revision, false);
     }
 
     private boolean safeRevert(String revision, boolean rollback) {
-        String head = store.getHead().getRecordId().toString();
+        String head = store.getRecordId(store.getHead());
         store.setRevision(revision);
         try {
             refreshModel();
             if (!rollback) {
                 setText("Switched head revision to " + revision);
             }
-        } catch (SegmentNotFoundException e) {
+        } catch (Exception e) {
             StringBuilder sb = new StringBuilder();
             sb.append("Unable to switch head revision to ");
             sb.append(revision);
@@ -642,44 +608,39 @@ public class NodeStoreTree extends JPane
             setText(sb.toString());
             return safeRevert(head, true);
         }
-        if (rollback) {
-            return false;
-        }
-        return true;
+        return !rollback;
     }
 
-    public void printPCMInfo() {
-        setText(new PCMAnalyser(store).toString());
+    void printPCMInfo() {
+        setText(store.getPersistedCompactionMapStats());
     }
 
     private static class NamePathModel implements Comparable<NamePathModel> {
 
-        private final FileStore store;
+        private final FileStoreWrapper store;
         private final String name;
         private final String path;
         private final boolean skipSizeCheck;
 
         private boolean loaded = false;
 
-        private Long[] size = { -1l, -1l };
+        private Long[] size = {-1L, -1L};
 
-        public NamePathModel(String name, String path, NodeState state,
-                Map<RecordIdKey, Long[]> sizeCache, boolean skipSizeCheck,
-                FileStore store) {
+        NamePathModel(String name, String path, NodeState state, Map<String, Long[]> sizeCache, boolean skipSizeCheck, FileStoreWrapper store) {
             this.store = store;
             this.name = name;
             this.path = path;
             this.skipSizeCheck = skipSizeCheck;
-            if (!skipSizeCheck && state instanceof SegmentNodeState) {
-                this.size = exploreSize((SegmentNodeState) state, sizeCache);
+            if (!skipSizeCheck && store.isPersisted(state)) {
+                this.size = exploreSize(state, sizeCache, store);
             }
         }
 
-        public void loaded() {
+        void loaded() {
             loaded = true;
         }
 
-        public boolean isLoaded() {
+        boolean isLoaded() {
             return loaded;
         }
 
@@ -737,13 +698,12 @@ public class NodeStoreTree extends JPane
         }
     }
 
-    private static Long[] exploreSize(SegmentNodeState ns,
-            Map<RecordIdKey, Long[]> sizeCache) {
-        RecordIdKey key = new RecordIdKey(ns.getRecordId());
+    private static Long[] exploreSize(NodeState ns, Map<String, Long[]> sizeCache, FileStoreWrapper store) {
+        String key = store.getRecordId(ns);
         if (sizeCache.containsKey(key)) {
             return sizeCache.get(key);
         }
-        Long[] s = { 0l, 0l };
+        Long[] s = {0L, 0L};
 
         List<String> names = newArrayList(ns.getChildNodeNames());
 
@@ -751,9 +711,7 @@ public class NodeStoreTree extends JPane
             List<String> temp = newArrayList();
             int poz = 0;
             // push 'root' to the beginning
-            Iterator<String> iterator = names.iterator();
-            while (iterator.hasNext()) {
-                String n = iterator.next();
+            for (String n : names) {
                 if (n.equals("root")) {
                     temp.add(poz, n);
                     poz++;
@@ -765,14 +723,14 @@ public class NodeStoreTree extends JPane
         }
 
         for (String n : names) {
-            SegmentNodeState k = (SegmentNodeState) ns.getChildNode(n);
-            RecordIdKey ckey = new RecordIdKey(k.getRecordId());
+            NodeState k = ns.getChildNode(n);
+            String ckey = store.getRecordId(k);
             if (sizeCache.containsKey(ckey)) {
                 // already been here, record size under 'link'
                 Long[] ks = sizeCache.get(ckey);
                 s[1] = s[1] + ks[0] + ks[1];
             } else {
-                Long[] ks = exploreSize(k, sizeCache);
+                Long[] ks = exploreSize(k, sizeCache, store);
                 s[0] = s[0] + ks[0];
                 s[1] = s[1] + ks[1];
             }
@@ -781,8 +739,7 @@ public class NodeStoreTree extends JPane
             for (int j = 0; j < ps.count(); j++) {
                 if (ps.getType().tag() == Type.BINARY.tag()) {
                     Blob b = ps.getValue(Type.BINARY, j);
-                    boolean skip = b instanceof SegmentBlob
-                            && ((SegmentBlob) b).isExternal();
+                    boolean skip = store.isExternal(b);
                     if (!skip) {
                         s[0] = s[0] + b.length();
                     }
@@ -795,38 +752,6 @@ public class NodeStoreTree extends JPane
         return s;
     }
 
-    private static class RecordIdKey {
-
-        private final long msb;
-        private final long lsb;
-        private final int offset;
-
-        public RecordIdKey(RecordId rid) {
-            this.offset = rid.getOffset();
-            this.msb = rid.getSegmentId().getMostSignificantBits();
-            this.lsb = rid.getSegmentId().getLeastSignificantBits();
-        }
-
-        @Override
-        public boolean equals(Object object) {
-            if (this == object) {
-                return true;
-            } else if (object instanceof RecordIdKey) {
-                RecordIdKey that = (RecordIdKey) object;
-                return offset == that.offset && msb == that.msb
-                        && lsb == that.lsb;
-            } else {
-                return false;
-            }
-        }
-
-        @Override
-        public int hashCode() {
-            return ((int) lsb) ^ offset;
-        }
-
-    }
-
     @Override
     public void close() throws IOException {
         store.close();

Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java?rev=1745235&r1=1745234&r2=1745235&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java Mon May 23 16:35:43 2016
@@ -18,6 +18,10 @@
 package org.apache.jackrabbit.oak.run;
 
 import static com.google.common.collect.Sets.newHashSet;
+import static com.google.common.collect.Sets.newTreeSet;
+import static com.google.common.escape.Escapers.builder;
+import static javax.jcr.PropertyType.BINARY;
+import static javax.jcr.PropertyType.STRING;
 import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
 import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.newBasicReadOnlyBlobStore;
 import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.openFileStore;
@@ -25,6 +29,7 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.plugins.segment.RecordType.NODE;
 import static org.apache.jackrabbit.oak.plugins.segment.SegmentGraph.writeGCGraph;
 import static org.apache.jackrabbit.oak.plugins.segment.SegmentGraph.writeSegmentGraph;
+import static org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStateHelper.getTemplateId;
 import static org.apache.jackrabbit.oak.plugins.segment.file.tooling.ConsistencyChecker.checkConsistency;
 import static org.apache.jackrabbit.oak.run.Utils.asCloseable;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -53,6 +58,9 @@ import com.google.common.collect.Maps;
 import com.google.common.collect.Queues;
 import com.google.common.io.Closer;
 import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.IOUtils;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
@@ -64,19 +72,24 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.segment.RecordId;
 import org.apache.jackrabbit.oak.plugins.segment.RecordUsageAnalyser;
 import org.apache.jackrabbit.oak.plugins.segment.Segment;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentBlob;
 import org.apache.jackrabbit.oak.plugins.segment.SegmentId;
 import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeState;
 import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentPropertyState;
 import org.apache.jackrabbit.oak.plugins.segment.SegmentTracker;
 import org.apache.jackrabbit.oak.plugins.segment.compaction.CompactionStrategy;
 import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
 import org.apache.jackrabbit.oak.plugins.segment.file.JournalReader;
 import org.apache.jackrabbit.oak.plugins.segment.file.tooling.RevisionHistory;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 
 class SegmentUtils {
 
+    private final static int MAX_CHAR_DISPLAY = Integer.getInteger("max.char.display", 60);
+
     private SegmentUtils() {
         // Prevent instantiation
     }
@@ -315,7 +328,7 @@ class SegmentUtils {
             if (hasrefs) {
                 System.out.println("SegmentNodeState references to " + f);
                 List<String> paths = new ArrayList<String>();
-                NodeStoreTree.filterNodeStates(uuids, paths, store.getHead(),
+                filterNodeStates(uuids, paths, store.getHead(),
                         "/");
                 for (String p : paths) {
                     System.out.println("  " + p);
@@ -392,5 +405,75 @@ class SegmentUtils {
             }
         }
     }
+
+    private static String displayString(String value) {
+        if (MAX_CHAR_DISPLAY > 0 && value.length() > MAX_CHAR_DISPLAY) {
+            value = value.substring(0, MAX_CHAR_DISPLAY) + "... ("
+                    + value.length() + " chars)";
+        }
+        String escaped = builder().setSafeRange(' ', '~')
+                .addEscape('"', "\\\"").addEscape('\\', "\\\\").build()
+                .escape(value);
+        return '"' + escaped + '"';
+    }
+
+    private static void filterNodeStates(Set<UUID> uuids, List<String> paths, SegmentNodeState state, String path) {
+        Set<String> localPaths = newTreeSet();
+        for (PropertyState ps : state.getProperties()) {
+            if (ps instanceof SegmentPropertyState) {
+                SegmentPropertyState sps = (SegmentPropertyState) ps;
+                RecordId recordId = sps.getRecordId();
+                UUID id = recordId.getSegmentId().asUUID();
+                if (uuids.contains(id)) {
+                    if (ps.getType().tag() == STRING) {
+                        String val = "";
+                        if (ps.count() > 0) {
+                            // only shows the first value, do we need more?
+                            val = displayString(ps.getValue(Type.STRING, 0));
+                        }
+                        localPaths.add(path + ps.getName() + " = " + val
+                                + " [SegmentPropertyState<" + ps.getType()
+                                + ">@" + recordId + "]");
+                    } else {
+                        localPaths.add(path + ps + " [SegmentPropertyState<"
+                                + ps.getType() + ">@" + recordId + "]");
+                    }
+
+                }
+                if (ps.getType().tag() == BINARY) {
+                    // look for extra segment references
+                    for (int i = 0; i < ps.count(); i++) {
+                        Blob b = ps.getValue(Type.BINARY, i);
+                        for (SegmentId sbid : SegmentBlob.getBulkSegmentIds(b)) {
+                            UUID bid = sbid.asUUID();
+                            if (!bid.equals(id) && uuids.contains(bid)) {
+                                localPaths.add(path + ps
+                                        + " [SegmentPropertyState<"
+                                        + ps.getType() + ">@" + recordId + "]");
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        RecordId stateId = state.getRecordId();
+        if (uuids.contains(stateId.getSegmentId().asUUID())) {
+            localPaths.add(path + " [SegmentNodeState@" + stateId + "]");
+        }
+
+        RecordId templateId = getTemplateId(state);
+        if (uuids.contains(templateId.getSegmentId().asUUID())) {
+            localPaths.add(path + "[Template@" + templateId + "]");
+        }
+        paths.addAll(localPaths);
+        for (ChildNodeEntry ce : state.getChildNodeEntries()) {
+            NodeState c = ce.getNodeState();
+            if (c instanceof SegmentNodeState) {
+                filterNodeStates(uuids, paths, (SegmentNodeState) c,
+                        path + ce.getName() + "/");
+            }
+        }
+    }
 
 }