Britt Park e5eb45da0f Introducing a new API call into AVMService:
LayeringDescriptor getLayeringInfo(version, path);

LayeringDescriptor has three methods:

isBackground() Is the looked up node a background node.
getPathAVMStore() Gets a descriptor for the store the path was looked up in.
getNativeAVMStore() Gets the store that the actual node was found in.



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3348 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2006-07-18 19:08:21 +00:00

929 lines
31 KiB
Java

/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.avm;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.SortedMap;
/**
* This or AVMStore are
* the implementors of the operations specified by AVMService.
* @author britt
*/
class AVMRepository
{
/**
* The single instance of AVMRepository.
*/
private static AVMRepository fgInstance;
/**
* The current lookup count.
*/
private ThreadLocal<Integer> fLookupCount;
/**
* The node id issuer.
*/
private Issuer fNodeIssuer;
/**
* The content id issuer;
*/
private Issuer fContentIssuer;
/**
* The layer id issuer.
*/
private Issuer fLayerIssuer;
/**
* The file storage directory.
*/
private String fStorage;
/**
* Create a new one. It's given issuers and a storage directory.
* @param nodeIssuer
* @param contentIssuer
* @param layerIssuer
* @param storage
*/
public AVMRepository(Issuer nodeIssuer,
Issuer contentIssuer,
Issuer layerIssuer,
String storage)
{
fStorage = storage;
fNodeIssuer = nodeIssuer;
fContentIssuer = contentIssuer;
fLayerIssuer = layerIssuer;
fLookupCount = new ThreadLocal<Integer>();
fgInstance = this;
}
/**
* Create a file.
* @param path The path to the containing directory.
* @param name The name for the new file.
*/
public OutputStream createFile(String path, String name)
{
fLookupCount.set(1);
String[] pathParts = SplitPath(path);
AVMStore rep = getAVMStoreByName(pathParts[0], true);
// fSession.get().lock(rep, LockMode.UPGRADE);
return rep.createFile(pathParts[1], name);
}
/**
* Create a file with the given File as content.
* @param path The path to the containing directory.
* @param name The name to give the file.
* @param data The file contents.
*/
public void createFile(String path, String name, File data)
{
fLookupCount.set(1);
String[] pathParts = SplitPath(path);
AVMStore rep = getAVMStoreByName(pathParts[0], true);
rep.createFile(pathParts[1], name, data);
}
/**
* Create a new directory.
* @param path The path to the containing directory.
* @param name The name to give the directory.
*/
public void createDirectory(String path, String name)
{
fLookupCount.set(1);
String[] pathParts = SplitPath(path);
AVMStore rep = getAVMStoreByName(pathParts[0], true);
rep.createDirectory(pathParts[1], name);
}
/**
* Create a new layered directory.
* @param srcPath The target indirection for the new layered directory.
* @param dstPath The path to the containing directory.
* @param name The name for the new directory.
*/
public void createLayeredDirectory(String srcPath, String dstPath,
String name)
{
if (dstPath.indexOf(srcPath) == 0)
{
throw new AVMCycleException("Cycle would be created.");
}
fLookupCount.set(1);
String[] pathParts = SplitPath(dstPath);
AVMStore rep = getAVMStoreByName(pathParts[0], true);
// fSession.get().lock(rep, LockMode.UPGRADE);
rep.createLayeredDirectory(srcPath, pathParts[1], name);
}
/**
* Create a new layered file.
* @param srcPath The target indirection for the new layered file.
* @param dstPath The path to the containing directory.
* @param name The name of the new layered file.
*/
public void createLayeredFile(String srcPath, String dstPath, String name)
{
fLookupCount.set(1);
String[] pathParts = SplitPath(dstPath);
AVMStore rep = getAVMStoreByName(pathParts[0], true);
rep.createLayeredFile(srcPath, pathParts[1], name);
}
/**
* Create a new AVMStore.
* @param name The name to give the new AVMStore.
*/
public void createAVMStore(String name)
{
try
{
getAVMStoreByName(name, false);
throw new AVMExistsException("AVMStore exists: " + name);
}
catch (AVMNotFoundException anf)
{
// Do nothing.
}
// Newing up the object causes it to be written to the db.
@SuppressWarnings("unused")
AVMStore rep = new AVMStoreImpl(this, name);
// Special handling for AVMStore creation.
NewInAVMStore newInRep = AVMContext.fgInstance.fNewInAVMStoreDAO.getByNode(rep.getRoot());
AVMContext.fgInstance.fNewInAVMStoreDAO.delete(newInRep);
}
/**
* Create a new branch.
* @param version The version to branch off.
* @param srcPath The path to make a branch from.
* @param dstPath The containing directory.
* @param name The name of the new branch.
*/
public void createBranch(int version, String srcPath, String dstPath, String name)
{
if (dstPath.indexOf(srcPath) == 0)
{
throw new AVMCycleException("Cycle would be created.");
}
// Lookup the src node.
fLookupCount.set(1);
String [] pathParts = SplitPath(srcPath);
AVMStore srcRepo = getAVMStoreByName(pathParts[0], false);
Lookup sPath = srcRepo.lookup(version, pathParts[1], false);
// Lookup the destination directory.
fLookupCount.set(1);
pathParts = SplitPath(dstPath);
AVMStore dstRepo = getAVMStoreByName(pathParts[0], true);
Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1], true);
DirectoryNode dirNode = (DirectoryNode)dPath.getCurrentNode();
AVMNode srcNode = sPath.getCurrentNode();
AVMNode dstNode = null;
// We do different things depending on what kind of thing we're
// branching from. I'd be considerably happier if we disallowed
// certain scenarios, but Jon won't let me :P (bhp).
if (srcNode.getType() == AVMNodeType.PLAIN_DIRECTORY)
{
dstNode = new PlainDirectoryNodeImpl((PlainDirectoryNode)srcNode, dstRepo);
}
else if (srcNode.getType() == AVMNodeType.LAYERED_DIRECTORY)
{
dstNode =
new LayeredDirectoryNodeImpl((LayeredDirectoryNode)srcNode, dstRepo);
((LayeredDirectoryNode)dstNode).setLayerID(issueLayerID());
}
else if (srcNode.getType() == AVMNodeType.LAYERED_FILE)
{
dstNode = new LayeredFileNodeImpl((LayeredFileNode)srcNode, dstRepo);
}
else // This is a plain file.
{
dstNode = new PlainFileNodeImpl((PlainFileNode)srcNode, dstRepo);
}
dstNode.setVersionID(dstRepo.getNextVersionID());
dstNode.setAncestor(srcNode);
dirNode.putChild(name, dstNode);
dirNode.updateModTime();
}
/**
* Get an output stream to a file.
* @param path The full path to the file.
* @return An OutputStream.
*/
public OutputStream getOutputStream(String path)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
AVMStore rep = getAVMStoreByName(pathParts[0], true);
return rep.getOutputStream(pathParts[1]);
}
/**
* Get a random access file from a file node.
* @param version The version id (read-only if not -1)
* @param path The path to the file.
* @param access The access mode for RandomAccessFile.
* @return A RandomAccessFile.
*/
public RandomAccessFile getRandomAccess(int version, String path, String access)
{
fLookupCount.set(1);
String[] pathParts = SplitPath(path);
AVMStore rep = getAVMStoreByName(pathParts[0], true);
return rep.getRandomAccess(version, pathParts[1], access);
}
/**
* Rename a node.
* @param srcPath Source containing directory.
* @param srcName Source name.
* @param dstPath Destination containing directory.
* @param dstName Destination name.
*/
public void rename(String srcPath, String srcName, String dstPath,
String dstName)
{
// This is about as ugly as it gets.
if (dstPath.indexOf(srcPath + srcName) == 0)
{
throw new AVMCycleException("Cyclic rename.");
}
fLookupCount.set(1);
String [] pathParts = SplitPath(srcPath);
AVMStore srcRepo = getAVMStoreByName(pathParts[0], true);
Lookup sPath = srcRepo.lookupDirectory(-1, pathParts[1], true);
DirectoryNode srcDir = (DirectoryNode)sPath.getCurrentNode();
AVMNode srcNode = srcDir.lookupChild(sPath, srcName, -1, true);
if (srcNode == null)
{
throw new AVMNotFoundException("Not found: " + srcName);
}
fLookupCount.set(1);
pathParts = SplitPath(dstPath);
AVMStore dstRepo = getAVMStoreByName(pathParts[0], true);
Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1], true);
DirectoryNode dstDir = (DirectoryNode)dPath.getCurrentNode();
AVMNode dstNode = dstDir.lookupChild(dPath, dstName, -1, true);
if (dstNode != null)
{
throw new AVMExistsException("Node exists: " + dstName);
}
// We've passed the check, so we can go ahead and do the rename.
if (srcNode.getType() == AVMNodeType.PLAIN_DIRECTORY)
{
// If the source is layered then the renamed thing needs to be layered also.
if (sPath.isLayered())
{
// If this is a rename happening in the same layer we make a new
// OverlayedDirectoryNode that is not a primary indirection layer.
// Otherwise we do make the new OverlayedDirectoryNode a primary
// Indirection layer. This complexity begs the question of whether
// we should allow renames from within one layer to within another
// layer. Allowing it makes the logic absurdly complex.
if (dPath.isLayered() && dPath.getTopLayer().equals(sPath.getTopLayer()))
{
dstNode = new LayeredDirectoryNodeImpl((PlainDirectoryNode)srcNode, dstRepo, sPath, false);
((LayeredDirectoryNode)dstNode).setLayerID(sPath.getTopLayer().getLayerID());
}
else
{
dstNode = new LayeredDirectoryNodeImpl((DirectoryNode)srcNode, dstRepo, sPath, srcName);
((LayeredDirectoryNode)dstNode).setLayerID(issueLayerID());
}
}
else
{
dstNode = new PlainDirectoryNodeImpl((PlainDirectoryNode)srcNode, dstRepo);
}
}
else if (srcNode.getType() == AVMNodeType.LAYERED_DIRECTORY)
{
// TODO I think I need to subdivide this logic again.
// based on whether the destination is a layer or not.
if (!sPath.isLayered() || (sPath.isInThisLayer() &&
srcDir.getType() == AVMNodeType.LAYERED_DIRECTORY &&
((LayeredDirectoryNode)srcDir).directlyContains(srcNode)))
{
// Use the simple 'copy' constructor.
dstNode =
new LayeredDirectoryNodeImpl((LayeredDirectoryNode)srcNode, dstRepo);
((LayeredDirectoryNode)dstNode).setLayerID(((LayeredDirectoryNode)srcNode).getLayerID());
}
else
{
// If the source node is a primary indirection, then the 'copy' constructor
// is used. Otherwise the alternate constructor is called and its
// indirection is calculated from it's source context.
if (((LayeredDirectoryNode)srcNode).getPrimaryIndirection())
{
dstNode =
new LayeredDirectoryNodeImpl((LayeredDirectoryNode)srcNode, dstRepo);
}
else
{
dstNode =
new LayeredDirectoryNodeImpl((DirectoryNode)srcNode, dstRepo, sPath, srcName);
}
// What needs to be done here is dependent on whether the
// rename is to a layered context. If so then it should get the layer id
// of its destination parent. Otherwise it should get a new layer
// id.
if (dPath.isLayered())
{
((LayeredDirectoryNode)dstNode).setLayerID(dPath.getTopLayer().getLayerID());
}
else
{
((LayeredDirectoryNode)dstNode).setLayerID(issueLayerID());
}
}
}
else if (srcNode.getType() == AVMNodeType.LAYERED_FILE)
{
dstNode = new LayeredFileNodeImpl((LayeredFileNode)srcNode, dstRepo);
}
else // This is a plain file node.
{
dstNode = new PlainFileNodeImpl((PlainFileNode)srcNode, dstRepo);
}
srcDir.removeChild(srcName);
srcDir.updateModTime();
dstNode.setVersionID(dstRepo.getNextVersionID());
dstDir.putChild(dstName, dstNode);
dstDir.updateModTime();
dstNode.setAncestor(srcNode);
}
/**
* Uncover a deleted name in a layered directory.
* @param dirPath The path to the layered directory.
* @param name The name to uncover.
*/
public void uncover(String dirPath, String name)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(dirPath);
AVMStore repo = getAVMStoreByName(pathParts[0], true);
repo.uncover(pathParts[1], name);
}
/**
* Snapshot the given repositories.
* @param repositories The list of AVMStore name to snapshot.
* @return A List of version ids for each newly snapshotted AVMStore.
*/
public List<Integer> createSnapshot(List<String> repositories)
{
List<Integer> result = new ArrayList<Integer>();
for (String repName : repositories)
{
AVMStore repo = getAVMStoreByName(repName, true);
result.add(repo.createSnapshot());
}
return result;
}
/**
* Create a snapshot of a single AVMStore.
* @param store The name of the repository.
* @return The version id of the newly snapshotted repository.
*/
public int createSnapshot(String storeName)
{
AVMStore store = getAVMStoreByName(storeName, true);
return store.createSnapshot();
}
/**
* Remove a node and everything underneath it.
* @param path The path to the containing directory.
* @param name The name of the node to remove.
*/
public void remove(String path, String name)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0], true);
store.removeNode(pathParts[1], name);
}
/**
* Get rid of all content that lives only in the given AVMStore.
* Also removes the AVMStore.
* @param name The name of the AVMStore to purge.
*/
@SuppressWarnings("unchecked")
public void purgeAVMStore(String name)
{
AVMStore store = getAVMStoreByName(name, true);
AVMNode root = store.getRoot();
root.setIsRoot(false);
VersionRootDAO vrDAO = AVMContext.fgInstance.fVersionRootDAO;
List<VersionRoot> vRoots = vrDAO.getAllInAVMStore(store);
for (VersionRoot vr : vRoots)
{
AVMNode node = vr.getRoot();
node.setIsRoot(false);
vrDAO.delete(vr);
}
List<NewInAVMStore> newGuys = AVMContext.fgInstance.fNewInAVMStoreDAO.getByAVMStore(store);
for (NewInAVMStore newGuy : newGuys)
{
AVMContext.fgInstance.fNewInAVMStoreDAO.delete(newGuy);
}
AVMContext.fgInstance.fAVMStoreDAO.delete(store);
}
/**
* Remove all content specific to a AVMRepository and version.
* @param name The name of the AVMStore.
* @param version The version to purge.
*/
public void purgeVersion(String name, int version)
{
AVMStore store = getAVMStoreByName(name, true);
store.purgeVersion(version);
}
/**
* Get an input stream from a file.
* @param version The version to look under.
* @param path The path to the file.
* @return An InputStream.
*/
public InputStream getInputStream(int version, String path)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0], false);
return store.getInputStream(version, pathParts[1]);
}
/**
* Get an InputStream from a given version of a file.
* @param desc The node descriptor.
* @return The InputStream.
*/
public InputStream getInputStream(AVMNodeDescriptor desc)
{
fLookupCount.set(1);
AVMNode node = AVMContext.fgInstance.fAVMNodeDAO.getByID(desc.getId());
if (node == null)
{
throw new AVMNotFoundException("Not found.");
}
if (node.getType() != AVMNodeType.PLAIN_FILE &&
node.getType() != AVMNodeType.LAYERED_FILE)
{
throw new AVMWrongTypeException("Not a file.");
}
FileNode file = (FileNode)node;
return file.getContentForRead().getInputStream();
}
/**
* Get a listing of a directory.
* @param version The version to look under.
* @param path The path to the directory.
* @return A List of FolderEntries.
*/
public SortedMap<String, AVMNodeDescriptor> getListing(int version, String path)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0], false);
return store.getListing(version, pathParts[1]);
}
/**
* Get a directory listing from a directory node descriptor.
* @param dir The directory node descriptor.
* @return A SortedMap listing.
*/
public SortedMap<String, AVMNodeDescriptor> getListing(AVMNodeDescriptor dir)
{
fLookupCount.set(1);
AVMNode node = AVMContext.fgInstance.fAVMNodeDAO.getByID(dir.getId());
if (node.getType() != AVMNodeType.LAYERED_DIRECTORY &&
node.getType() != AVMNodeType.PLAIN_DIRECTORY)
{
throw new AVMWrongTypeException("Not a directory.");
}
DirectoryNode dirNode = (DirectoryNode)node;
return dirNode.getListing(dir);
}
/**
* Get descriptors of all AVMStores.
* @return A list of all descriptors.
*/
@SuppressWarnings("unchecked")
public List<AVMStoreDescriptor> getAVMStores()
{
List<AVMStore> l = AVMContext.fgInstance.fAVMStoreDAO.getAll();
List<AVMStoreDescriptor> result = new ArrayList<AVMStoreDescriptor>();
for (AVMStore store : l)
{
result.add(store.getDescriptor());
}
return result;
}
/**
* Get a descriptor for an AVMStore.
* @param name The name to get.
* @return The descriptor.
*/
public AVMStoreDescriptor getAVMStore(String name)
{
AVMStore store = getAVMStoreByName(name, false);
return store.getDescriptor();
}
/**
* Get all version for a given AVMStore.
* @param name The name of the AVMStore.
* @return A Set will all the version ids.
*/
public List<VersionDescriptor> getAVMStoreVersions(String name)
{
AVMStore store = getAVMStoreByName(name, false);
return store.getVersions();
}
/**
* Get the set of versions between (inclusive) of the given dates.
* From or to may be null but not both.
* @param name The name of the AVMRepository.
* @param from The earliest date.
* @param to The latest date.
* @return The Set of version IDs.
*/
public List<VersionDescriptor> getAVMStoreVersions(String name, Date from, Date to)
{
AVMStore store = getAVMStoreByName(name, false);
return store.getVersions(from, to);
}
/**
* Issue a node id.
* @return The new id.
*/
public long issueID()
{
return fNodeIssuer.issue();
}
/**
* Issue a content id.
* @return The new id.
*/
public long issueContentID()
{
return fContentIssuer.issue();
}
/**
* Issue a new layer id.
* @return The new id.
*/
public long issueLayerID()
{
return fLayerIssuer.issue();
}
/**
* Get the indirection path for a layered node.
* @param version The version to look under.
* @param path The path to the node.
* @return The indirection path.
*/
public String getIndirectionPath(int version, String path)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0], false);
return store.getIndirectionPath(version, pathParts[1]);
}
/**
* Get the next version id for the given AVMStore.
* @param name The name of the AVMStore.
* @return The next version id.
*/
public int getLatestVersionID(String name)
{
AVMStore store = getAVMStoreByName(name, false);
return store.getNextVersionID();
}
/**
* Get an AVMStore by name.
* @param name The name of the AVMStore.
* @param write Whether this is called for a write operation.
* @return The AVMStore.
*/
private AVMStore getAVMStoreByName(String name, boolean write)
{
AVMStore store = AVMContext.fgInstance.fAVMStoreDAO.getByName(name);
if (store == null)
{
throw new AVMNotFoundException("AVMStore not found: " + name);
}
return store;
}
/**
* Get a descriptor for an AVMStore root.
* @param version The version to get.
* @param name The name of the AVMStore.
* @return The descriptor for the root.
*/
public AVMNodeDescriptor getAVMStoreRoot(int version, String name)
{
AVMStore store = getAVMStoreByName(name, false);
if (store == null)
{
throw new AVMNotFoundException("Not found: " + name);
}
return store.getRoot(version);
}
// TODO Fix this awful mess regarding cycle detection.
/**
* Lookup a node.
* @param version The version to look under.
* @param path The path to lookup.
* @return A lookup object.
*/
public Lookup lookup(int version, String path)
{
Integer count = fLookupCount.get();
if (count == null)
{
fLookupCount.set(1);
}
else
{
fLookupCount.set(count + 1);
}
if (fLookupCount.get() > 50)
{
throw new AVMCycleException("Cycle in lookup.");
}
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0], false);
Lookup result = store.lookup(version, pathParts[1], false);
if (count == null)
{
fLookupCount.set(null);
}
return result;
}
/**
* Lookup a descriptor from a directory descriptor.
* @param dir The directory descriptor.
* @param name The name of the child to lookup.
* @return The child's descriptor.
*/
public AVMNodeDescriptor lookup(AVMNodeDescriptor dir, String name)
{
fLookupCount.set(0);
AVMNode node = AVMContext.fgInstance.fAVMNodeDAO.getByID(dir.getId());
if (node == null)
{
throw new AVMNotFoundException("Not found: " + dir.getId());
}
if (node.getType() != AVMNodeType.LAYERED_DIRECTORY &&
node.getType() != AVMNodeType.PLAIN_DIRECTORY)
{
throw new AVMWrongTypeException("Not a directory.");
}
DirectoryNode dirNode = (DirectoryNode)node;
return dirNode.lookupChild(dir, name);
}
/**
* Get information about layering of a path.
* @param version The version to look under.
* @param path The full avm path.
* @return A LayeringDescriptor.
*/
public LayeringDescriptor getLayeringInfo(int version, String path)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0], false);
Lookup lookup = store.lookup(version, pathParts[1], false);
return new LayeringDescriptor(!lookup.getDirectlyContained(),
lookup.getAVMStore().getDescriptor(),
lookup.getFinalStore().getDescriptor());
}
/**
* Lookup a directory specifically.
* @param version The version to look under.
* @param path The path to lookup.
* @return A lookup object.
*/
public Lookup lookupDirectory(int version, String path)
{
fLookupCount.set(fLookupCount.get() + 1);
if (fLookupCount.get() > 50)
{
throw new AVMCycleException("Cycle in lookup.");
}
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0], false);
return store.lookupDirectory(version, pathParts[1], false);
}
/**
* Utility to split a path, foo:bar/baz into its repository and path parts.
* @param path The fully qualified path.
* @return The repository name and the repository path.
*/
private String[] SplitPath(String path)
{
String [] pathParts = path.split(":");
if (pathParts.length != 2)
{
throw new AVMException("Invalid path: " + path);
}
return pathParts;
}
/**
* Get the path to file storage.
* @return The root path of file storage.
*/
public String getStorageRoot()
{
return fStorage;
}
/**
* Make a directory into a primary indirection.
* @param path The full path.
*/
public void makePrimary(String path)
{
fLookupCount.set(1);
String[] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0], true);
store.makePrimary(pathParts[1]);
}
/**
* Change what a layered directory points at.
* @param path The full path to the layered directory.
* @param target The new target path.
*/
public void retargetLayeredDirectory(String path, String target)
{
fLookupCount.set(1);
String[] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0], true);
store.retargetLayeredDirectory(pathParts[1], target);
}
/**
* Get the history chain for a node.
* @param desc The node to get history of.
* @param count The maximum number of ancestors to traverse. Negative means all.
* @return A List of ancestors.
*/
public List<AVMNodeDescriptor> getHistory(AVMNodeDescriptor desc, int count)
{
AVMNode node = AVMContext.fgInstance.fAVMNodeDAO.getByID(desc.getId());
if (node == null)
{
throw new AVMNotFoundException("Not found.");
}
if (count < 0)
{
count = Integer.MAX_VALUE;
}
List<AVMNodeDescriptor> history = new ArrayList<AVMNodeDescriptor>();
for (int i = 0; i < count; i++)
{
node = node.getAncestor();
if (node == null)
{
break;
}
history.add(node.getDescriptor("UNKNOWN", "UNKNOWN", "UNKNOWN"));
}
return history;
}
/**
* Set the opacity of a layered directory. An opaque directory hides
* the things it points to via indirection.
* @param path The path to the layered directory.
* @param opacity True is opaque; false is not.
*/
public void setOpacity(String path, boolean opacity)
{
fLookupCount.set(1);
String[] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0], true);
store.setOpacity(pathParts[1], opacity);
}
/**
* Get the AVMStoreDescriptor for an AVMStore.
* @param name The name of the AVMStore.
* @return The descriptor.
*/
public AVMStoreDescriptor getAVMStoreDescriptor(String name)
{
return getAVMStoreByName(name, false).getDescriptor();
}
/**
* Get the common ancestor of two nodes if one exists. Unfortunately
* this is a quadratic problem, taking time proportional to the product
* of the lengths of the left and right history chains.
* @param left The first node.
* @param right The second node.
* @return The common ancestor. There are four possible results. Null means
* that there is no common ancestor. Left returned means that left is strictly
* an ancestor of right. Right returned means that right is strictly an
* ancestor of left. Any other non null return is the common ancestor and
* indicates that left and right are in conflict.
*/
public AVMNodeDescriptor getCommonAncestor(AVMNodeDescriptor left,
AVMNodeDescriptor right)
{
AVMNode lNode = AVMContext.fgInstance.fAVMNodeDAO.getByID(left.getId());
AVMNode rNode = AVMContext.fgInstance.fAVMNodeDAO.getByID(right.getId());
if (lNode == null || rNode == null)
{
throw new AVMNotFoundException("Node not found.");
}
List<AVMNode> leftHistory = new ArrayList<AVMNode>();
while (lNode != null)
{
leftHistory.add(lNode);
lNode = lNode.getAncestor();
}
List<AVMNode> rightHistory = new ArrayList<AVMNode>();
while (rNode != null)
{
rightHistory.add(rNode);
rNode = rNode.getAncestor();
}
for (AVMNode l : leftHistory)
{
for (AVMNode r : rightHistory)
{
if (l.equals(r))
{
return l.getDescriptor("", "", "");
}
}
}
return null;
}
/**
* Get the single instance of AVMRepository.
* @return The single instance.
*/
public static AVMRepository GetInstance()
{
return fgInstance;
}
}