blob: 09935efe959517d1479428ee0f29531a6fbf48ff [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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
*******************************************************************************/
package org.eclipse.team.internal.ccvs.core.resources;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.*;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.client.*;
import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
public class RemoteModule extends RemoteFolder {
private String label;
private ICVSRemoteResource[] referencedModules;
private LocalOption[] localOptions;
private boolean expandable;
public static RemoteModule[] getRemoteModules(ICVSRepositoryLocation repository, CVSTag tag, IProgressMonitor monitor) throws TeamException {
monitor = Policy.monitorFor(monitor);
monitor.beginTask(CVSMessages.RemoteModule_getRemoteModules, 100);
try {
RemoteModule[] modules;
Session s = new Session(repository, getRemoteRootFolder(repository), false);
s.open(Policy.subMonitorFor(monitor, 10), false /* read-only */);
try {
modules = Command.CHECKOUT.getRemoteModules(s, tag, Policy.subMonitorFor(monitor, 90));
} finally {
s.close();
}
return modules;
} finally {
monitor.done();
}
}
private static ICVSFolder getRemoteRootFolder(ICVSRepositoryLocation repository) {
return new RemoteFolder(null, repository, "/", null); //$NON-NLS-1$
}
/**
* Create a set of RemoteModules from the provided module definition strings returned from the server
*
* At the moment, we are very restrictive on the types of modules we support.
*/
public static RemoteModule[] createRemoteModules(String[] moduleDefinitionStrings, ICVSRepositoryLocation repository, CVSTag tag) {
Map modules = new HashMap();
Map referencedModulesTable = new HashMap();
Map moduleAliases = new HashMap();
// First pass: Create the remote module instances based on remote mapping
for (int i = 0; i < moduleDefinitionStrings.length; i++) {
// Read the module name
StringTokenizer tokenizer = new StringTokenizer(moduleDefinitionStrings[i]);
String moduleName = tokenizer.nextToken();
List<LocalOption> localOptionsList;
String next;
try {
// Read the options associated with the module
localOptionsList = new ArrayList<>();
next = tokenizer.nextToken();
while (next.charAt(0) == '-') {
switch (next.charAt(1)) {
case 'a': // alias
localOptionsList.add(Checkout.ALIAS);
break;
case 'l': // don't recurse
localOptionsList.add(Command.DO_NOT_RECURSE);
break;
case 'd': // directory
localOptionsList.add(Checkout.makeDirectoryNameOption(tokenizer.nextToken()));
break;
case 'e':
case 'i':
case 'o':
case 't':
case 'u': // Ignore any programs
tokenizer.nextToken();
break;
case 's': // status
localOptionsList.add(Checkout.makeStatusOption(tokenizer.nextToken()));
break;
default: // unanticipated option. Ignore it and go on
}
next = tokenizer.nextToken();
}
} catch (NoSuchElementException e) {
// There is an invalid entry in the modules file. Log it and continue
CVSProviderPlugin.log(IStatus.WARNING, NLS.bind(CVSMessages.RemoteModule_invalidDefinition, new String[] { moduleDefinitionStrings[i], repository.getLocation(true) }), null);
continue;
}
LocalOption[] localOptions = localOptionsList.toArray(new LocalOption[localOptionsList.size()]);
if (Checkout.ALIAS.isElementOf(localOptions)) {
if (localOptions.length > 1) {
// XXX This is an error condition that needs to be reported
}
// An alias expands to one or more modules or paths
List expansions = new ArrayList(10);
expansions.add(next);
while (tokenizer.hasMoreTokens())
expansions.add(tokenizer.nextToken());
moduleAliases.put(moduleName, expansions.toArray(new String[expansions.size()]));
modules.put(moduleName, new RemoteModule(moduleName, null, repository, null, localOptions, tag, true));
} else {
// The module definition may have a leading directory which can be followed by some files
if (!(next.charAt(0) == '&')) {
String directory = next;
List files = new ArrayList();
while (tokenizer.hasMoreTokens() && (next.charAt(0) != '&')) {
next = tokenizer.nextToken() ;
if ((next.charAt(0) != '&'))
files.add(next);
}
RemoteModule remoteModule = new RemoteModule(moduleName, null, repository, directory, localOptions, tag, ! files.isEmpty());
modules.put(moduleName, remoteModule);
if ( ! files.isEmpty()) {
ICVSRemoteResource[] children = new ICVSRemoteResource[files.size()];
for (int j = 0; j < children.length; j++) {
children[j] = new RemoteFile(remoteModule, Update.STATE_NONE, (String)files.get(j), null, null, tag);
remoteModule.setChildren(children);
}
}
} else {
modules.put(moduleName, new RemoteModule(moduleName, null, repository, null, localOptions, tag, true));
}
// Record any referenced modules so that can be cross-referenced below
if (next.charAt(0) == '&') {
List<String> children = new ArrayList<>(10);
children.add(next);
while (tokenizer.hasMoreTokens())
children.add(tokenizer.nextToken());
referencedModulesTable.put(moduleName, children.toArray(new String[children.size()]));
}
}
}
// Second pass: Cross reference aliases to modules
// XXX Aliases can reference other aliases which confuses the expansion!
Iterator iter = moduleAliases.keySet().iterator();
while (iter.hasNext()) {
String moduleName = (String)iter.next();
RemoteModule module = (RemoteModule)modules.get(moduleName);
String[] expansion = (String[])moduleAliases.get(moduleName);
List referencedFolders = new ArrayList();
boolean expandable = true;
for (int i = 0; i < expansion.length; i++) {
if (expansion[i].charAt(0) == '!') {
// XXX Unsupported for now
expandable = false;
} else {
IPath path = new Path(null, expansion[i]);
if (path.segmentCount() > 1) {
// XXX Unsupported for now
expandable = false;
} else {
RemoteModule child = (RemoteModule)modules.get(expansion[i]);
if (child == null) {
referencedFolders.add(new RemoteFolder(null, repository, path.toString(), tag));
} else {
// Need to check if the child is a module alias
if (child.isAlias()) {
// XXX Unsupported for now
expandable = false;
} else {
referencedFolders.add(child);
}
}
}
}
}
if (expandable) {
//TODO: Make module static??
module.setChildren((ICVSRemoteResource[]) referencedFolders.toArray(new ICVSRemoteResource[referencedFolders.size()]));
} else {
module.setExpandable(false);
}
}
// Third pass: Cross reference remote modules where necessary
iter = modules.keySet().iterator();
while (iter.hasNext()) {
String moduleName = (String)iter.next();
String[] children = (String[])referencedModulesTable.get(moduleName);
if (children != null) {
RemoteModule module = (RemoteModule)modules.get(moduleName);
List<RemoteModule> referencedFolders = new ArrayList<>();
boolean expandable = true;
for (int i = 0; i < children.length; i++) {
RemoteModule child = (RemoteModule)modules.get(children[i].substring(1));
if (child == null) {
// invalid module definition
expandable = false;
} else if (child.isAlias()) {
// Include alias children in-line
expandable = false;
// referencedFolders.addAll(Arrays.asList(child.getChildren()));
} else {
// XXX not expandable if child has local directory option (-d)
if (Command.findOption(child.getLocalOptions(), "-d") != null) { //$NON-NLS-1$
expandable = false;
} else {
referencedFolders.add(child);
}
}
}
if (expandable) {
module.setReferencedModules(referencedFolders.toArray(new ICVSRemoteResource[referencedFolders.size()]));
} else {
module.setExpandable(false);
}
}
}
return (RemoteModule[])modules.values().toArray(new RemoteModule[modules.size()]);
}
public RemoteModule(String label, RemoteFolder parent, ICVSRepositoryLocation repository, String repositoryRelativePath, LocalOption[] localOptions, CVSTag tag, boolean isStatic) {
super(parent,
label,
repository,
repositoryRelativePath == null ? FolderSyncInfo.VIRTUAL_DIRECTORY : repositoryRelativePath,
tag,
isStatic);
this.localOptions = localOptions;
this.label = label;
this.expandable = true;
}
public LocalOption[] getLocalOptions() {
return localOptions;
}
/*
* Override of inherited getMembers in order to combine the physical members with any referenced modules
*/
public ICVSRemoteResource[] getMembers(CVSTag tagName, IProgressMonitor monitor) throws CVSException {
if ( ! expandable) return new ICVSRemoteResource[0];
ICVSRemoteResource[] physicalChildren;
if ( folderInfo.getIsStatic()) {
physicalChildren = getChildren();
} else {
physicalChildren = super.getMembers(tagName, monitor);
}
ICVSRemoteResource[] allChildren;
if (referencedModules != null && referencedModules.length > 0) {
if (physicalChildren == null) {
allChildren = referencedModules;
} else {
// Combine two sets of children
allChildren = new ICVSRemoteResource[physicalChildren.length + referencedModules.length];
for (int i = 0; i < physicalChildren.length; i++) {
allChildren[i] = physicalChildren[i];
}
for (int i = 0; i < referencedModules.length; i++) {
allChildren[i + physicalChildren.length] = referencedModules[i];
}
}
} else if (physicalChildren != null) {
allChildren = physicalChildren;
} else {
allChildren = new ICVSRemoteResource[0];
}
return allChildren;
}
private void setReferencedModules(ICVSRemoteResource[] referencedModules) {
this.referencedModules = referencedModules;
}
public boolean isAlias() {
return Checkout.ALIAS.isElementOf(localOptions);
}
public boolean isExpandable() {
return expandable;
}
private void setExpandable(boolean expandable) {
this.expandable = expandable;
}
public ICVSRemoteResource forTag(ICVSRemoteFolder parent, CVSTag tagName) {
RemoteModule r = new RemoteModule(label, (RemoteFolder)parent, getRepository(), folderInfo.getRepository(), localOptions, tagName, folderInfo.getIsStatic());
r.setExpandable(expandable);
if (folderInfo.getIsStatic()) {
ICVSRemoteResource[] children = getChildren();
if (children != null) {
List taggedChildren = new ArrayList(children.length);
for (int i = 0; i < children.length; i++) {
ICVSRemoteResource resource = children[i];
taggedChildren.add(((RemoteResource)resource).forTag(r, tagName));
}
r.setChildren((ICVSRemoteResource[]) taggedChildren.toArray(new ICVSRemoteResource[taggedChildren.size()]));
}
}
if (referencedModules != null) {
List taggedModules = new ArrayList(referencedModules.length);
for (int i = 0; i < referencedModules.length; i++) {
RemoteModule module = (RemoteModule)referencedModules[i];
taggedModules.add(module.forTag(r, tagName));
}
r.setReferencedModules((ICVSRemoteResource[]) taggedModules.toArray(new ICVSRemoteResource[taggedModules.size()]));
}
return r;
}
public boolean isDefinedModule() {
return true;
}
public boolean equals(Object arg0) {
if (arg0 instanceof RemoteModule) {
RemoteModule module = (RemoteModule) arg0;
return (getName().equals(module.getName()) && super.equals(module));
}
return false;
}
public int hashCode() {
return super.hashCode() | getName().hashCode();
}
public ICVSResource getChild(String path) throws CVSException {
if (path.equals(Session.CURRENT_LOCAL_FOLDER) || path.length() == 0)
return this;
// If the path is one segment and it's a referenced module, return the module
// Note: the overriden method will extract the first segment from a multi segment
// path and re-invoke this method so we only need to check for one segment here
// and use the inherited method in the other cases
if (referencedModules != null) {
if (path.indexOf(Session.SERVER_SEPARATOR) == -1) {
for (int i=0;i<referencedModules.length;i++) {
if (referencedModules[i].getName().equals(path))
return referencedModules[i];
}
}
}
return super.getChild(path);
}
}