/* * 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.core.persistence.util; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.jcr.PropertyType; import org.apache.jackrabbit.core.id.NodeId; import org.apache.jackrabbit.core.value.InternalValue; import org.apache.jackrabbit.spi.Name; /** * Holds structural information about a node. Used by the consistency checker and garbage collector. */ public final class NodeInfo { /** * The same node id in a NodeInfo graph typically occurs three times: as a the id of the current * NodeInfo, as the parent to another NodeInfo, and as a child of another NodeInfo. In order to * minimize the memory footprint use an NodeId object pool. */ private static final ConcurrentMap<NodeId,NodeId> nodeIdPool = new ConcurrentHashMap<NodeId, NodeId>(1000); /** * The node id */ private final NodeId nodeId; /** * The parent node id */ private final NodeId parentId; /** * The child ids */ private List<NodeId> children; /** * Map of reference property names of this node with their node id values */ private Map<Name, List<NodeId>> references; /** * Whether this node is referenceable or not */ private boolean isReferenceable; /** * Whether this node has blob properties in data storage */ private boolean hasBlobsInDataStore; /** * Create a new NodeInfo object from a bundle * * @param bundle the node bundle */ public NodeInfo(final NodePropBundle bundle) { nodeId = getNodeId(bundle.getId()); parentId = getNodeId(bundle.getParentId()); List<NodePropBundle.ChildNodeEntry> childNodeEntries = bundle.getChildNodeEntries(); if (!childNodeEntries.isEmpty()) { children = new ArrayList<NodeId>(childNodeEntries.size()); for (NodePropBundle.ChildNodeEntry entry : bundle.getChildNodeEntries()) { children.add(getNodeId(entry.getId())); } } else { children = Collections.emptyList(); } for (NodePropBundle.PropertyEntry entry : bundle.getPropertyEntries()) { if (entry.getType() == PropertyType.REFERENCE) { if (references == null) { references = new HashMap<Name, List<NodeId>>(4); } List<NodeId> values = new ArrayList<NodeId>(entry.getValues().length); for (InternalValue value : entry.getValues()) { values.add(getNodeId(value.getNodeId())); } references.put(entry.getName(), values); } else if (entry.getType() == PropertyType.BINARY) { for (InternalValue internalValue : entry.getValues()) { if (internalValue.isInDataStore()) { hasBlobsInDataStore = true; break; } } } } if (references == null) { references = Collections.emptyMap(); } isReferenceable = bundle.isReferenceable(); } /** * @return the node id of this node */ public NodeId getId() { return nodeId; } /** * @return the parent id of this node */ public NodeId getParentId() { return parentId; } /** * @return the child ids of this node */ public List<NodeId> getChildren() { return children; } /** * @return the reference properties along with their node id values of this node */ public Map<Name, List<NodeId>> getReferences() { return references; } /** * @return whether the node represented by this node info is referenceable */ public boolean isReferenceable() { return isReferenceable; } /** * @return whether the node has blob properties that are inside the data storage */ public boolean hasBlobsInDataStore() { return hasBlobsInDataStore; } /** * Simple pool implementation to minimize memory overhead from node id objects * @param nodeId node id to cache * @return the cached node id */ private static NodeId getNodeId(NodeId nodeId) { if (nodeId == null) { return null; } NodeId cached = nodeIdPool.get(nodeId); if (cached == null) { cached = nodeIdPool.putIfAbsent(nodeId, nodeId); if (cached == null) { cached = nodeId; } } return cached; } /** * Clear the NodeId pool. */ public static void clearPool() { nodeIdPool.clear(); } }