/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.netbeans.lib.cvsclient.command.export;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.netbeans.lib.cvsclient.ClientServices;
import org.netbeans.lib.cvsclient.command.Builder;
import org.netbeans.lib.cvsclient.command.CommandException;
import org.netbeans.lib.cvsclient.command.KeywordSubstitutionOptions;
import org.netbeans.lib.cvsclient.command.RepositoryCommand;
import org.netbeans.lib.cvsclient.connection.AuthenticationException;
import org.netbeans.lib.cvsclient.event.EventManager;
import org.netbeans.lib.cvsclient.event.MessageEvent;
import org.netbeans.lib.cvsclient.request.ArgumentRequest;
import org.netbeans.lib.cvsclient.request.CommandRequest;
import org.netbeans.lib.cvsclient.request.DirectoryRequest;
/**
* The export command exports the projects (modules in the repository) to the local directory structure.
*
* @author MIlos Kleint
*/
public class ExportCommand extends RepositoryCommand {
/**
* A store of potentially empty directories. When a directory has a file in it, it is removed from this set. This set allows the prune
* option to be implemented.
*/
private final Set emptyDirectories = new HashSet();
private boolean pruneDirectories;
private KeywordSubstitutionOptions keywordSubstitutionOptions;
/** Holds value of property exportByDate. */
private String exportByDate;
/** Holds value of property exportByRevision. */
private String exportByRevision;
/** Holds value of property exportDirectory. */
private String exportDirectory;
/** Holds value of property useHeadIfNotFound. */
private boolean useHeadIfNotFound;
/** Don't shorten module paths if -d specified. */
private boolean notShortenPaths;
/** Do not run module program (if any). */
private boolean notRunModuleProgram;
public ExportCommand() {
resetCVSCommand();
}
/**
* Returns the keyword substitution option.
*/
public KeywordSubstitutionOptions getKeywordSubstitutionOptions() {
return keywordSubstitutionOptions;
}
/**
* Sets the keywords substitution option.
*/
public void setKeywordSubstitutionOptions(KeywordSubstitutionOptions keywordSubstitutionOptions) {
this.keywordSubstitutionOptions = keywordSubstitutionOptions;
}
/**
* Set whether to prune directories. This is the -P option in the command-line CVS.
*/
public void setPruneDirectories(boolean pruneDirectories) {
this.pruneDirectories = pruneDirectories;
}
/**
* Get whether to prune directories.
*
* @return true if directories should be removed if they contain no files, false otherwise.
*/
public boolean isPruneDirectories() {
return pruneDirectories;
}
/**
* Execute this command
*
* @param client
* the client services object that provides any necessary services to this command, including the ability to actually process
* all the requests
*/
@Override
protected void postExpansionExecute(ClientServices client, EventManager em) throws CommandException, AuthenticationException {
//
// moved modules code to the end of the other arguments --GAR
//
final int FIRST_INDEX = 0;
final int SECOND_INDEX = 1;
if (!isRecursive()) {
requests.add(FIRST_INDEX, new ArgumentRequest("-l")); // NOI18N
}
if (useHeadIfNotFound) {
requests.add(FIRST_INDEX, new ArgumentRequest("-f")); // NOI18N
}
if (exportDirectory != null && !exportDirectory.equals("")) {
requests.add(FIRST_INDEX, new ArgumentRequest("-d")); // NOI18N
requests.add(SECOND_INDEX, new ArgumentRequest(getExportDirectory()));
}
if (exportByDate != null && exportByDate.length() > 0) {
requests.add(FIRST_INDEX, new ArgumentRequest("-D")); // NOI18N
requests.add(SECOND_INDEX, new ArgumentRequest(getExportByDate()));
}
if (exportByRevision != null && exportByRevision.length() > 0) {
requests.add(FIRST_INDEX, new ArgumentRequest("-r")); // NOI18N
requests.add(SECOND_INDEX, new ArgumentRequest(getExportByRevision()));
}
if (notShortenPaths) {
requests.add(FIRST_INDEX, new ArgumentRequest("-N")); // NOI18N
}
if (notRunModuleProgram) {
requests.add(FIRST_INDEX, new ArgumentRequest("-n")); // NOI18N
}
if (getKeywordSubstitutionOptions() != null) {
requests.add(new ArgumentRequest("-k" + getKeywordSubstitutionOptions())); // NOI18N
}
addArgumentRequests();
requests.add(new DirectoryRequest(".", client.getRepository())); // NOI18N
requests.add(CommandRequest.EXPORT);
try {
client.processRequests(requests);
if (pruneDirectories) {
pruneEmptyDirectories();
}
requests.clear();
} catch (CommandException ex) {
throw ex;
} catch (Exception ex) {
throw new CommandException(ex, ex.getLocalizedMessage());
} finally {
removeAllCVSAdminFiles();
}
}
private void removeAllCVSAdminFiles() {
File rootDirect = null;
if (getExportDirectory() != null) {
rootDirect = new File(getLocalDirectory(), getExportDirectory());
deleteCVSSubDirs(rootDirect);
} else {
rootDirect = new File(getLocalDirectory());
Iterator mods = expandedModules.iterator();
while (mods.hasNext()) {
String mod = mods.next().toString();
File modRoot = new File(rootDirect.getAbsolutePath(), mod);
deleteCVSSubDirs(modRoot);
}
}
}
private void deleteCVSSubDirs(File root) {
if (root.isDirectory()) {
File[] subDirs = root.listFiles();
if (subDirs == null) {
return;
}
for (int i = 0; i < subDirs.length; i++) {
if (subDirs[i].isDirectory()) {
if (subDirs[i].getName().equalsIgnoreCase("CVS")) { // NOI18N
final File[] adminFiles = subDirs[i].listFiles();
for (int j = 0; j < adminFiles.length; j++) {
adminFiles[j].delete();
}
subDirs[i].delete();
} else {
deleteCVSSubDirs(subDirs[i]);
}
}
}
}
}
@Override
public String getCVSCommand() {
StringBuffer toReturn = new StringBuffer("export "); // NOI18N
toReturn.append(getCVSArguments());
if (modules != null && modules.size() > 0) {
for (Iterator it = modules.iterator(); it.hasNext();) {
String module = (String) it.next();
toReturn.append(module);
toReturn.append(' ');
}
} else {
String localizedMsg = CommandException.getLocalMessage("ExportCommand.moduleEmpty.text"); // NOI18N
toReturn.append(" "); // NOI18N
toReturn.append(localizedMsg);
}
return toReturn.toString();
}
@Override
public String getCVSArguments() {
StringBuffer toReturn = new StringBuffer(""); // NOI18N
if (!isRecursive()) {
toReturn.append("-l "); // NOI18N
}
if (isUseHeadIfNotFound()) {
toReturn.append("-f "); // NOI18N
}
if (getExportByDate() != null) {
toReturn.append("-D "); // NOI18N
toReturn.append(getExportByDate());
toReturn.append(" "); // NOI18N
}
if (getExportByRevision() != null) {
toReturn.append("-r "); // NOI18N
toReturn.append(getExportByRevision());
toReturn.append(" "); // NOI18N
}
if (isPruneDirectories()) {
toReturn.append("-P "); // NOI18N
}
if (isNotShortenPaths()) {
toReturn.append("-N "); // NOI18N
}
if (isNotRunModuleProgram()) {
toReturn.append("-n "); // NOI18N
}
if (getExportDirectory() != null) {
toReturn.append("-d "); // NOI18N
toReturn.append(getExportDirectory());
toReturn.append(" "); // NOI18N
}
if (getKeywordSubstitutionOptions() != null) {
toReturn.append("-k"); // NOI18N
toReturn.append(getKeywordSubstitutionOptions().toString());
toReturn.append(" "); // NOI18N
}
return toReturn.toString();
}
@Override
public boolean setCVSCommand(char opt, String optArg) {
if (opt == 'k') {
setKeywordSubstitutionOptions(KeywordSubstitutionOptions.findKeywordSubstOption(optArg));
} else if (opt == 'r') {
setExportByRevision(optArg);
} else if (opt == 'f') {
setUseHeadIfNotFound(true);
} else if (opt == 'D') {
setExportByDate(optArg);
} else if (opt == 'd') {
setExportDirectory(optArg);
} else if (opt == 'P') {
setPruneDirectories(true);
} else if (opt == 'N') {
setNotShortenPaths(true);
} else if (opt == 'n') {
setNotRunModuleProgram(true);
} else if (opt == 'l') {
setRecursive(false);
} else if (opt == 'R') {
setRecursive(true);
} else {
return false;
}
return true;
}
@Override
public void resetCVSCommand() {
setModules(null);
setKeywordSubstitutionOptions(null);
setPruneDirectories(false);
setRecursive(true);
setExportByDate(null);
setExportByRevision(null);
setExportDirectory(null);
setUseHeadIfNotFound(false);
setNotShortenPaths(false);
setNotRunModuleProgram(false);
}
@Override
public String getOptString() {
return "k:r:D:NPlRnd:f"; // NOI18N
}
/**
* Creates the ExportBuilder.
*/
@Override
public Builder createBuilder(EventManager eventManager) {
return new ExportBuilder(eventManager, this);
}
/**
* Called when the server wants to send a message to be displayed to the user. The message is only for information purposes and clients
* can choose to ignore these messages if they wish.
*
* @param e
* the event
*/
@Override
public void messageSent(MessageEvent e) {
super.messageSent(e);
// we use this event to determine which directories need to be checked
// for updating
if (pruneDirectories && e.getMessage().indexOf(": Exporting ") > 0) { // NOI18N
File file = new File(getLocalDirectory(), e.getMessage().substring(21));
emptyDirectories.add(file);
}
}
/**
* Prunes a directory, recursively pruning its subdirectories
*
* @param directory
* the directory to prune
*/
private boolean pruneEmptyDirectory(File directory) throws IOException {
boolean empty = true;
final File[] contents = directory.listFiles();
// should never be null, but just in case...
if (contents != null) {
for (int i = 0; i < contents.length; i++) {
if (contents[i].isFile()) {
empty = false;
} else {
if (!contents[i].getName().equals("CVS")) { // NOI18N
empty = pruneEmptyDirectory(contents[i]);
}
}
if (!empty) {
break;
}
}
if (empty) {
// check this is a CVS directory and not some directory the user
// has stupidly called CVS...
final File entriesFile = new File(directory, "CVS/Entries"); // NOI18N
if (entriesFile.exists()) {
final File adminDir = new File(directory, "CVS"); // NOI18N
final File[] adminFiles = adminDir.listFiles();
for (int i = 0; i < adminFiles.length; i++) {
adminFiles[i].delete();
}
adminDir.delete();
directory.delete();
}
}
}
return empty;
}
/**
* Remove any directories that don't contain any files
*/
private void pruneEmptyDirectories() throws IOException {
final Iterator it = emptyDirectories.iterator();
while (it.hasNext()) {
final File dir = (File) it.next();
// we might have deleted it already (due to recursive delete)
// so we need to check existence
if (dir.exists()) {
pruneEmptyDirectory(dir);
}
}
emptyDirectories.clear();
}
/**
* Getter for property exportByDate.
*
* @return Value of property exportByDate.
*/
public String getExportByDate() {
return this.exportByDate;
}
/**
* Setter for property exportByDate.
*
* @param exportByDate
* New value of property exportByDate.
*/
public void setExportByDate(String exportByDate) {
this.exportByDate = exportByDate;
}
/**
* Getter for property exportByRevision.
*
* @return Value of property exportByRevision.
*/
public String getExportByRevision() {
return this.exportByRevision;
}
/**
* Setter for property exportByRevision.
*
* @param exportByRevision
* New value of property exportByRevision.
*/
public void setExportByRevision(String exportByRevision) {
this.exportByRevision = exportByRevision;
}
/**
* Getter for property exportDirectory.
*
* @return Value of property exportDirectory.
*/
public String getExportDirectory() {
return this.exportDirectory;
}
/**
* Setter for property exportDirectory.
*
* @param exportDirectory
* New value of property exportDirectory.
*/
public void setExportDirectory(String exportDirectory) {
this.exportDirectory = exportDirectory;
}
/**
* Getter for property useHeadIfNotFound.
*
* @return Value of property useHeadIfNotFound.
*/
public boolean isUseHeadIfNotFound() {
return this.useHeadIfNotFound;
}
/**
* Setter for property useHeadIfNotFound.
*
* @param useHeadIfNotFound
* New value of property useHeadIfNotFound.
*/
public void setUseHeadIfNotFound(boolean useHeadIfNotFound) {
this.useHeadIfNotFound = useHeadIfNotFound;
}
/**
* Getter for property notShortenPaths.
*
* @return Value of property notShortenPaths.
*/
public boolean isNotShortenPaths() {
return notShortenPaths;
}
/**
* Setter for property notShortenPaths.
*
* @param notShortenPaths
* New value of property notShortenPaths.
*/
public void setNotShortenPaths(boolean notShortenPaths) {
this.notShortenPaths = notShortenPaths;
}
/**
* Getter for property notRunModuleProgram.
*
* @return Value of property notRunModuleProgram.
*/
public boolean isNotRunModuleProgram() {
return notRunModuleProgram;
}
/**
* Setter for property notRunModuleProgram.
*
* @param notRunModuleProgram
* New value of property notRunModuleProgram.
*/
public void setNotRunModuleProgram(boolean notRunModuleProgram) {
this.notRunModuleProgram = notRunModuleProgram;
}
}