Copyright (c) 2004, 2011 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation James Blackburn (Broadcom Corp.) - ongoing development
/******************************************************************************* * Copyright (c) 2004, 2011 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * James Blackburn (Broadcom Corp.) - ongoing development *******************************************************************************/
package org.eclipse.core.internal.localstore; import java.io.*; import org.eclipse.core.internal.localstore.Bucket.Visitor; import org.eclipse.core.internal.resources.ResourceException; import org.eclipse.core.internal.resources.Workspace; import org.eclipse.core.internal.utils.Messages; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; import org.eclipse.osgi.util.NLS;
Since:3,1
/** * @since 3,1 */
public class BucketTree { public static final int DEPTH_INFINITE = Integer.MAX_VALUE; public static final int DEPTH_ONE = 1; public static final int DEPTH_ZERO = 0; private final static int SEGMENT_QUOTA = 256; //two hex characters
Store all bucket names to avoid creating garbage when traversing the tree
/** * Store all bucket names to avoid creating garbage when traversing the tree */
private static final char[][] HEX_STRINGS; static { HEX_STRINGS = new char[SEGMENT_QUOTA][]; for (int i = 0; i < HEX_STRINGS.length; i++) HEX_STRINGS[i] = Integer.toHexString(i).toCharArray(); } protected Bucket current; private Workspace workspace; public BucketTree(Workspace workspace, Bucket bucket) { this.current = bucket; this.workspace = workspace; }
From a starting point in the tree, visit all nodes under it.
Params:
  • visitor –
  • base –
  • depth –
/** * From a starting point in the tree, visit all nodes under it. * @param visitor * @param base * @param depth */
public void accept(Bucket.Visitor visitor, IPath base, int depth) throws CoreException { if (Path.ROOT.equals(base)) { current.load(null, locationFor(Path.ROOT)); if (current.accept(visitor, base, DEPTH_ZERO) != Visitor.CONTINUE) return; if (depth == DEPTH_ZERO) return; boolean keepVisiting = true; depth--; IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN); for (int i = 0; keepVisiting && i < projects.length; i++) { IPath projectPath = projects[i].getFullPath(); keepVisiting = internalAccept(visitor, projectPath, locationFor(projectPath), depth, 1); } } else internalAccept(visitor, base, locationFor(base), depth, 0); } public void close() throws CoreException { current.save(); saveVersion(); } public Bucket getCurrent() { return current; } public File getVersionFile() { return new File(locationFor(Path.ROOT), current.getVersionFileName()); }
This will never be called for a bucket for the workspace root.
Returns:whether to continue visiting other branches
/** * This will never be called for a bucket for the workspace root. * * @return whether to continue visiting other branches */
private boolean internalAccept(Bucket.Visitor visitor, IPath base, File bucketDir, int depthRequested, int currentDepth) throws CoreException { current.load(base.segment(0), bucketDir); int outcome = current.accept(visitor, base, depthRequested); if (outcome != Visitor.CONTINUE) return outcome == Visitor.RETURN; if (depthRequested <= currentDepth) return true; File[] subDirs = bucketDir.listFiles(); if (subDirs == null) return true; for (File subDir : subDirs) { if (subDir.isDirectory()) { if (!internalAccept(visitor, base, subDir, depthRequested, currentDepth + 1)) { return false; } } } return true; } public void loadBucketFor(IPath path) throws CoreException { current.load(Path.ROOT.equals(path) ? null : path.segment(0), locationFor(path)); } private File locationFor(IPath resourcePath) { //optimized to avoid string and path creations IPath baseLocation = workspace.getMetaArea().locationFor(resourcePath).removeTrailingSeparator(); int segmentCount = resourcePath.segmentCount(); String locationString = baseLocation.toOSString(); StringBuilder locationBuffer = new StringBuilder(locationString.length() + Bucket.INDEXES_DIR_NAME.length() + 16); locationBuffer.append(locationString); locationBuffer.append(File.separatorChar); locationBuffer.append(Bucket.INDEXES_DIR_NAME); // the last segment is ignored for (int i = 1; i < segmentCount - 1; i++) { // translate all segments except the first one (project name) locationBuffer.append(File.separatorChar); locationBuffer.append(translateSegment(resourcePath.segment(i))); } return new File(locationBuffer.toString()); }
Writes the version tag to a file on disk.
/** * Writes the version tag to a file on disk. */
private void saveVersion() throws CoreException { File versionFile = getVersionFile(); if (!versionFile.getParentFile().exists()) versionFile.getParentFile().mkdirs(); try (FileOutputStream stream = new FileOutputStream(versionFile)) { stream.write(current.getVersion()); } catch (IOException e) { String message = NLS.bind(Messages.resources_writeWorkspaceMeta, versionFile.getAbsolutePath()); throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, null, message, e); } } private char[] translateSegment(String segment) { // String.hashCode algorithm is API return HEX_STRINGS[Math.abs(segment.hashCode()) % SEGMENT_QUOTA]; } }