/*
* (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.openflexo.fps;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.lib.cvsclient.admin.Entry;
import org.netbeans.lib.cvsclient.command.CommandAbortedException;
import org.netbeans.lib.cvsclient.command.CommandException;
import org.netbeans.lib.cvsclient.command.KeywordSubstitutionOptions;
import org.netbeans.lib.cvsclient.command.add.AddCommand;
import org.netbeans.lib.cvsclient.command.commit.CommitCommand;
import org.netbeans.lib.cvsclient.command.remove.RemoveCommand;
import org.netbeans.lib.cvsclient.command.update.UpdateCommand;
import org.netbeans.lib.cvsclient.connection.AuthenticationException;
import org.netbeans.lib.cvsclient.event.BinaryMessageEvent;
import org.netbeans.lib.cvsclient.event.CVSListener;
import org.netbeans.lib.cvsclient.event.FileAddedEvent;
import org.netbeans.lib.cvsclient.event.FileInfoEvent;
import org.netbeans.lib.cvsclient.event.FileRemovedEvent;
import org.netbeans.lib.cvsclient.event.FileToRemoveEvent;
import org.netbeans.lib.cvsclient.event.FileUpdatedEvent;
import org.netbeans.lib.cvsclient.event.MessageEvent;
import org.netbeans.lib.cvsclient.event.ModuleExpansionEvent;
import org.netbeans.lib.cvsclient.event.TerminationEvent;
import org.openflexo.toolbox.FileUtils;
import org.openflexo.toolbox.FlexoRunnable;
public abstract class CVSAbstractFile extends CVSObject {
protected static final Logger logger = Logger.getLogger(SharedProject.class.getPackage().getName());
private File _localFile;
private CVSContainer _container;
protected SharedProject _sharedProject;
public CVSAbstractFile(File localFile, SharedProject sharedProject) {
super();
_sharedProject = sharedProject;
try {
_localFile = localFile.getCanonicalFile();
} catch (IOException e) {
logger.warning("Could not retrieve cannonical file for " + localFile);
e.printStackTrace();
_localFile = localFile;
}
}
public File getFile() {
return _localFile;
}
public String getFileName() {
if (_localFile != null) {
return _localFile.getName();
}
return "???";
}
public CVSAbstractFile getCVSAbstractFile(File aFile) {
if (aFile.equals(getFile())) {
return this;
}
if (!FileUtils.isFileContainedIn(aFile, getFile())) {
return null;
}
if (this instanceof CVSContainer) {
for (CVSFile f : ((CVSContainer) this).getFiles()) {
if (f.getFile().equals(aFile)) {
// logger.info("Return "+f);
return f;
}
}
for (CVSDirectory d : ((CVSContainer) this).getDirectories()) {
CVSAbstractFile returned = d.getCVSAbstractFile(aFile);
if (returned != null) {
return returned;
}
}
}
// logger.info("Return null");
return null;
}
@Override
public String toString() {
return getClass().getSimpleName() + ":" + getFile().getAbsolutePath();
}
private CVSStatus _derivedStatus = null;
@Override
public CVSStatus getDerivedStatus() {
if (_derivedStatus == null) {
_derivedStatus = computeDerivedStatus();
}
return _derivedStatus;
}
private CVSStatus computeDerivedStatus() {
CVSStatus returned = CVSStatus.UpToDate;
if (this instanceof CVSContainer) {
CVSContainer container = (CVSContainer) this;
Vector<CVSAbstractFile> files = new Vector<CVSAbstractFile>();
files.addAll(container.getFiles());
files.addAll(container.getDirectories());
for (CVSAbstractFile f : files) {
CVSStatus status = f.getDerivedStatus();
if (status.isConflicting()) {
returned = CVSStatus.Conflicting;
} else if (status.isLocallyModified()) {
if (returned.isConflicting()) {
// OK, nothing to do
} else if (returned.isRemotelyModified()) {
returned = CVSStatus.Conflicting;
} else if (returned.isLocallyModified()) {
if (returned != status) {
returned = CVSStatus.LocallyModified;
}
} else if (returned.isUpToDate()) {
returned = status;
}
} else if (status.isRemotelyModified()) {
if (returned.isConflicting()) {
// OK, nothing to do
} else if (returned.isRemotelyModified()) {
if (returned != status) {
returned = CVSStatus.RemotelyModified;
}
} else if (returned.isLocallyModified()) {
returned = CVSStatus.Conflicting;
} else if (returned.isUpToDate()) {
returned = status;
}
}
}
} else if (this instanceof CVSFile) {
returned = ((CVSFile) this).getStatus();
}
return returned;
}
public void notifyStatusChanged(CVSAbstractFile file) {
CVSStatus newDerivedStatus = computeDerivedStatus();
if (newDerivedStatus != _derivedStatus) {
_derivedStatus = newDerivedStatus;
setChanged();
if (getContainer() != null) {
getContainer().notifyStatusChanged(this);
}
}
}
public CVSContainer getContainer() {
return _container;
}
public void setContainer(CVSContainer container) {
_container = container;
}
public SharedProject getSharedProject() {
return _sharedProject;
}
@Override
public boolean isContainedIn(FPSObject obj) {
if (obj instanceof CVSRepositoryList) {
return getSharedProject().getCVSRepository().isContainedIn(obj);
} else if (obj instanceof CVSRepository) {
return getSharedProject().getCVSRepository() == obj;
} else if (obj instanceof CVSModule) {
return getSharedProject().getCVSModule() == obj;
} else if (obj instanceof SharedProject) {
return getSharedProject() == obj;
} else if (obj instanceof CVSAbstractFile) {
CVSAbstractFile current = this;
while (current != null) {
if (current == obj) {
return true;
}
if (current.getContainer() instanceof CVSAbstractFile) {
current = (CVSAbstractFile) current.getContainer();
} else {
current = null;
}
}
}
return false;
}
// ***************************************************************************
// **************************** Committing stuff *****************************
// ***************************************************************************
public interface CommitListener {
public void notifyCommitFinished(CVSFile file, CommitStatus status);
}
private CommitListener _commitListener;
private CommittingThread _committingThread;
public synchronized void commit(String commitMessage, CommitListener commitListener, CVSFile... files) {
if (_committingThread == null) {
Vector<CVSFile> filesToCommit = new Vector<CVSFile>();
if (this instanceof CVSContainer) {
for (CVSFile f : files) {
if (((CVSContainer) this).getFiles().contains(f)) {
filesToCommit.add(f);
} else {
logger.warning("Exclude " + f + " from files to commit since not belonging to current directory");
}
}
} else if (this instanceof CVSFile && files.length == 1 && files[0] == this) {
filesToCommit.add(files[0]);
}
logger.info("Committing " + filesToCommit + " in directory " + getFile());
_committingThread = new CommittingThread(commitMessage, filesToCommit);
getSharedProject().addToThreadPool(_committingThread);
_commitListener = commitListener;
}
}
private synchronized void notifyCommitFinished(CVSFile file, Entry entry) {
file.setEntry(entry);
if (this instanceof CVSContainer && file.getStatus() == CVSStatus.LocallyRemoved) {
((CVSContainer) this).removeFromFiles(file);
}
file.setStatus(CVSStatus.UpToDate);
if (_commitListener != null) {
_commitListener.notifyCommitFinished(file, CommitStatus.OK);
}
}
private synchronized void notifyCommitFailed(CVSFile file) {
if (_commitListener != null) {
_commitListener.notifyCommitFinished(file, CommitStatus.Error);
}
}
public synchronized boolean isCommitting() {
return _committingThread != null;
}
public static enum CommitStatus {
NotFinished {
/**
* Overrides toString
*
* @see java.lang.Enum#toString()
*/
@Override
public String toString() {
return "NotFinished";
}
},
OK {
/**
* Overrides toString
*
* @see java.lang.Enum#toString()
*/
@Override
public String toString() {
return "OK";
}
},
Error {
/**
* Overrides toString
*
* @see java.lang.Enum#toString()
*/
@Override
public String toString() {
return "Error";
}
}
}
private class CommittingThread implements FlexoRunnable, CVSListener {
private CommitStatus status;
private Vector<CVSFile> _filesToCommit;
private String _commitMessage;
protected CommittingThread(String commitMessage, Vector<CVSFile> filesToCommit) {
_filesToCommit = filesToCommit;
_commitMessage = commitMessage;
}
@Override
public void run() {
if (logger.isLoggable(Level.INFO)) {
logger.info("Starting to commit " + _filesToCommit);
}
status = CommitStatus.OK;
// Handle cases where the directory itself doesn't exist
if (CVSAbstractFile.this instanceof CVSDirectory) {
logger.fine("Committing in directory " + CVSAbstractFile.this.getFileName());
if (!((CVSDirectory) CVSAbstractFile.this).existsOnCVSRepository()) {
logger.fine("Add non-existing directory " + CVSAbstractFile.this.getFileName());
cvsAddDirectory();
}
}
// Add required files if any
try {
cvsAddTextFiles();
cvsAddBinaryFiles();
} catch (Exception e) {
e.printStackTrace();
}
// Remove required files if any
cvsRemove();
CVSConnection connection = null;
if (status == CommitStatus.OK) {
status = CommitStatus.NotFinished;
CommitCommand commitCommand = new CommitCommand();
File[] files = new File[_filesToCommit.size()];
for (int i = 0; i < _filesToCommit.size(); i++) {
files[i] = _filesToCommit.get(i).getFile();
}
commitCommand.setFiles(files);
commitCommand.setMessage(_commitMessage);
try {
connection = getSharedProject().openConnection();
connection.getClient().getEventManager().addCVSListener(this);
connection.getClient().setLocalPath(getFile().getCanonicalPath());
connection.executeCommand(commitCommand);
} catch (CommandAbortedException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (CommandException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (AuthenticationException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (IOException e) {
e.printStackTrace();
status = CommitStatus.Error;
}
}
if (status == CommitStatus.OK) {
for (CVSFile f : _filesToCommit) {
try {
notifyCommitFinished(f, connection.getClient().getEntry(f.getFile()));
} catch (IOException e) {
logger.warning("Unexpected exception " + e.getMessage());
e.printStackTrace();
notifyCommitFailed(f);
}
}
} else if (status == CommitStatus.Error) {
for (CVSFile f : _filesToCommit) {
notifyCommitFailed(f);
}
}
if (logger.isLoggable(Level.INFO)) {
logger.info("End of commit " + _filesToCommit + ": " + status);
}
_committingThread = null;
}
private void cvsAddTextFiles() {
Vector<CVSFile> filesToAdd = new Vector<CVSFile>();
// logger.info("Add text files: ");
for (CVSFile f : _filesToCommit) {
if (f.getStatus() == CVSStatus.LocallyAdded && !f.isBinary()) {
filesToAdd.add(f);
// logger.info("TEXT file: "+f.getFileName());
}
}
if (filesToAdd.size() == 0) {
status = CommitStatus.OK;
return;
}
status = CommitStatus.NotFinished;
AddCommand addCommand = new AddCommand();
File[] files = new File[filesToAdd.size()];
for (int i = 0; i < filesToAdd.size(); i++) {
files[i] = filesToAdd.get(i).getFile();
}
addCommand.setFiles(files);
try {
CVSConnection connection = getSharedProject().openConnection();
connection.getClient().getEventManager().addCVSListener(this);
connection.getClient().setLocalPath(getFile().getCanonicalPath());
connection.executeCommand(addCommand);
} catch (CommandAbortedException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (CommandException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (AuthenticationException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (IOException e) {
e.printStackTrace();
status = CommitStatus.Error;
}
}
private void cvsAddBinaryFiles() {
Vector<CVSFile> filesToAdd = new Vector<CVSFile>();
// logger.info("Add binary files: ");
for (CVSFile f : _filesToCommit) {
if (f.getStatus() == CVSStatus.LocallyAdded && f.isBinary()) {
filesToAdd.add(f);
// logger.info("BINARY file: "+f.getFileName());
}
}
if (filesToAdd.size() == 0) {
status = CommitStatus.OK;
return;
}
status = CommitStatus.NotFinished;
AddCommand addCommand = new AddCommand();
File[] files = new File[filesToAdd.size()];
for (int i = 0; i < filesToAdd.size(); i++) {
files[i] = filesToAdd.get(i).getFile();
}
addCommand.setFiles(files);
addCommand.setKeywordSubst(KeywordSubstitutionOptions.BINARY);
try {
CVSConnection connection = getSharedProject().openConnection();
connection.getClient().getEventManager().addCVSListener(this);
connection.getClient().setLocalPath(getFile().getCanonicalPath());
connection.executeCommand(addCommand);
} catch (CommandAbortedException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (CommandException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (AuthenticationException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (IOException e) {
e.printStackTrace();
status = CommitStatus.Error;
}
}
private Vector<File> appendFiles(File dir, Vector<File> reply) {
File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isFile();
}
});
for (int i = 0; i < files.length; i++) {
reply.add(files[i]);
}
return reply;
}
private Vector<File> appendInnerDirectories(File dir, Vector<File> reply) {
File[] dirs = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory() && !pathname.getName().equals("CVS");
}
});
for (int i = 0; i < dirs.length; i++) {
reply.add(dirs[i]);
appendInnerDirectories(dirs[i], reply);
appendFiles(dirs[i], reply);
}
return reply;
}
private void cvsAddDirectory() {
status = CommitStatus.NotFinished;
AddCommand addCommand = new AddCommand();
Vector<File> dirsToAdd = new Vector<File>();
dirsToAdd.add(getFile());
appendInnerDirectories(getFile(), dirsToAdd);
File[] files = new File[dirsToAdd.size()];
int i = 0;
Enumeration<File> en = dirsToAdd.elements();
while (en.hasMoreElements()) {
files[i] = en.nextElement();
i++;
}
// File[] files = new File[1];
// files[0] = getFile();
addCommand.setFiles(files);
try {
CVSConnection connection = getSharedProject().openConnection();
connection.getClient().getEventManager().addCVSListener(this);
connection.getClient().setLocalPath(getFile().getParentFile().getCanonicalPath());
connection.executeCommand(addCommand);
} catch (CommandAbortedException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (CommandException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (AuthenticationException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (IOException e) {
e.printStackTrace();
status = CommitStatus.Error;
}
}
private void cvsRemove() {
Vector<CVSFile> filesToRemove = new Vector<CVSFile>();
for (CVSFile f : _filesToCommit) {
if (f.getStatus() == CVSStatus.LocallyRemoved) {
filesToRemove.add(f);
}
}
if (filesToRemove.size() == 0) {
status = CommitStatus.OK;
return;
}
status = CommitStatus.NotFinished;
RemoveCommand removeCommand = new RemoveCommand();
File[] files = new File[filesToRemove.size()];
for (int i = 0; i < filesToRemove.size(); i++) {
files[i] = filesToRemove.get(i).getFile();
}
removeCommand.setFiles(files);
try {
CVSConnection connection = getSharedProject().openConnection();
connection.getClient().getEventManager().addCVSListener(this);
connection.getClient().setLocalPath(getFile().getCanonicalPath());
connection.executeCommand(removeCommand);
} catch (CommandAbortedException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (CommandException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (AuthenticationException e) {
e.printStackTrace();
status = CommitStatus.Error;
} catch (IOException e) {
e.printStackTrace();
status = CommitStatus.Error;
}
}
@Override
public void commandTerminated(TerminationEvent e) {
status = e.isError() ? CommitStatus.Error : CommitStatus.OK;
}
@Override
public void fileAdded(FileAddedEvent e) {
}
@Override
public void fileInfoGenerated(FileInfoEvent e) {
}
@Override
public void fileRemoved(FileRemovedEvent e) {
}
@Override
public void fileToRemove(FileToRemoveEvent e) {
}
@Override
public void fileUpdated(FileUpdatedEvent e) {
}
@Override
public void messageSent(MessageEvent e) {
}
@Override
public void messageSent(BinaryMessageEvent e) {
}
@Override
public void moduleExpanded(ModuleExpansionEvent e) {
}
/**
* Overrides getName
*
* @see org.openflexo.toolbox.FlexoRunnable#getName()
*/
@Override
public String getName() {
return "Committing in " + getFile().getName();
}
}
// ***************************************************************************
// **************************** Updating stuff *****************************
// ***************************************************************************
public interface UpdateListener {
public void notifyUpdateFinished(CVSFile file, UpdateStatus status);
}
private UpdateListener _updateListener;
private UpdatingThread _updatingThread;
// WARNING: override option (update -C) not implemented on CVS on MacOSX1.4 !!! :-(
public synchronized UpdatingThread update(UpdateListener updateListener, boolean override, CVSFile... files) {
if (_updatingThread == null) {
Vector<CVSFile> filesToUpdate = new Vector<CVSFile>();
if (this instanceof CVSContainer) {
for (CVSFile f : files) {
if (((CVSContainer) this).getFiles().contains(f)) {
filesToUpdate.add(f);
} else {
logger.warning("Exclude " + f + " from files to update since not belonging to current directory");
}
}
} else if (this instanceof CVSFile && files.length == 1 && files[0] == this) {
filesToUpdate.add(files[0]);
}
logger.info("Updating " + filesToUpdate + " in directory " + getFile());
_updatingThread = new UpdatingThread(filesToUpdate, override);
getSharedProject().addToThreadPool(_updatingThread);
_updateListener = updateListener;
return _updatingThread;
}
return null;
}
private synchronized void notifyUpdateFinished(CVSFile file, Entry entry) {
file.setEntry(entry);
/*if (this instanceof CVSContainer
&& file.getStatus() == CVSStatus.RemotelyRemoved) {
((CVSContainer)this).removeFromFiles(file);
}
file.setStatus(CVSStatus.UpToDate);*/
if (_updateListener != null) {
_updateListener.notifyUpdateFinished(file, UpdateStatus.OK);
}
}
private synchronized void notifyUpdateFailed(CVSFile file) {
if (_updateListener != null) {
_updateListener.notifyUpdateFinished(file, UpdateStatus.Error);
}
}
public synchronized boolean isUpdating() {
return _updatingThread != null;
}
public static enum UpdateStatus {
NotFinished, OK, Error
}
public class UpdatingThread implements CVSListener, FlexoRunnable {
private UpdateStatus status;
private Vector<CVSFile> _filesToUpdate;
private boolean _override;
private CVSConnection connection = null;
protected UpdatingThread(Vector<CVSFile> filesToUpdate, boolean override) {
_filesToUpdate = filesToUpdate;
_override = override;
}
public void abort() {
if (connection != null && connection.getClient() != null) {
connection.getClient().abort();
}
}
@Override
public void run() {
if (logger.isLoggable(Level.INFO)) {
logger.info("Starting to update " + _filesToUpdate);
}
connection = null;
status = UpdateStatus.NotFinished;
UpdateCommand updateCommand = new UpdateCommand();
File[] files = new File[_filesToUpdate.size()];
for (int i = 0; i < _filesToUpdate.size(); i++) {
files[i] = _filesToUpdate.get(i).getFile();
}
updateCommand.setFiles(files);
updateCommand.setCleanCopy(_override);
try {
connection = getSharedProject().openConnection();
connection.getClient().getEventManager().addCVSListener(this);
connection.getClient().setLocalPath(getFile().getCanonicalPath());
connection.executeCommand(updateCommand);
} catch (CommandAbortedException e) {
e.printStackTrace();
status = UpdateStatus.Error;
} catch (CommandException e) {
e.printStackTrace();
status = UpdateStatus.Error;
} catch (AuthenticationException e) {
e.printStackTrace();
status = UpdateStatus.Error;
} catch (IOException e) {
e.printStackTrace();
status = UpdateStatus.Error;
}
if (status == UpdateStatus.OK) {
for (CVSFile f : _filesToUpdate) {
try {
notifyUpdateFinished(f, connection.getClient().getEntry(f.getFile()));
} catch (IOException e) {
logger.warning("Unexpected exception " + e.getMessage());
e.printStackTrace();
notifyUpdateFailed(f);
}
}
} else if (status == UpdateStatus.Error) {
for (CVSFile f : _filesToUpdate) {
notifyUpdateFailed(f);
}
}
if (logger.isLoggable(Level.INFO)) {
logger.info("End of commit " + _filesToUpdate + ": " + status);
}
_updatingThread = null;
}
@Override
public void commandTerminated(TerminationEvent e) {
status = e.isError() ? UpdateStatus.Error : UpdateStatus.OK;
}
@Override
public void fileAdded(FileAddedEvent e) {
// logger.info("fileAdded() "+e);
}
@Override
public void fileInfoGenerated(FileInfoEvent e) {
// logger.info("fileInfoGenerated() "+e);
}
@Override
public void fileRemoved(FileRemovedEvent e) {
// logger.info("fileRemoved() "+e);
}
@Override
public void fileToRemove(FileToRemoveEvent e) {
// logger.info("fileToRemove() "+e);
}
@Override
public void fileUpdated(FileUpdatedEvent e) {
// logger.info("fileUpdated() "+e);
}
@Override
public void messageSent(MessageEvent e) {
// logger.info("messageSent() "+e);
}
@Override
public void messageSent(BinaryMessageEvent e) {
// logger.info("messageSent() "+e);
}
@Override
public void moduleExpanded(ModuleExpansionEvent e) {
// logger.info("moduleExpanded() "+e);
}
/**
* Overrides getName
*
* @see org.openflexo.toolbox.FlexoRunnable#getName()
*/
@Override
public String getName() {
return "Updating in " + getFile().getName();
}
}
}