package org.openflexo.builders; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.InvalidPropertiesFormatException; import java.util.List; import java.util.Properties; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import org.openflexo.builders.exception.MissingArgumentException; import org.openflexo.builders.utils.FlexoBuilderEditor; import org.openflexo.builders.utils.FlexoBuilderListener; import org.openflexo.builders.utils.FlexoCVSConsoleListener; import org.openflexo.foundation.DefaultFlexoEditor; import org.openflexo.foundation.FlexoEditor; import org.openflexo.foundation.FlexoEditor.FlexoEditorFactory; import org.openflexo.foundation.rm.FlexoProject; import org.openflexo.foundation.rm.FlexoResourceManager; import org.openflexo.foundation.rm.FlexoStorageResource; import org.openflexo.foundation.rm.StorageResourceData; import org.openflexo.foundation.utils.ProjectInitializerException; import org.openflexo.foundation.utils.ProjectLoadingCancelledException; import org.openflexo.fps.CVSFile; import org.openflexo.fps.CVSRepository; import org.openflexo.fps.CVSRepositoryList; import org.openflexo.fps.FPSObject; import org.openflexo.fps.SharedProject; import org.openflexo.fps.action.CommitFiles; import org.openflexo.fps.action.MarkAsMergedFiles; import org.openflexo.fps.action.OverrideAndCommitFiles; import org.openflexo.fps.action.ShareProject; import org.openflexo.fps.action.SynchronizeWithRepository; import org.openflexo.fps.action.UpdateFiles; import org.openflexo.logging.FlexoLogger; import org.openflexo.toolbox.FileUtils; public class FlexoProjectMergeMain extends FlexoExternalMain { protected static final Logger logger = FlexoLogger.getLogger(FlexoProjectMergeMain.class.getPackage().getName()); public static final int CONFLICTING_FILES = -9; public static final String FIRST_COMMIT_ARGUMENT = "-FirstCommit"; public static final String PROJECT_DIRECTORY_ARGUMENT_PREFIX = "-Directory="; public static final String CVS_REPOSITORY_FILE_ARGUMENT_PREFIX = "-CVSRepository="; public static final String OVERRIDE_ARGUMENT = "-Override"; public static final String MODULE_NAME_ARGUMENT_PREFIX = "-ModuleName="; public static final String TAG_ARGUMENT_PREFIX = "-Tag="; public static final String COMMENT_ARGUMENT_PREFIX = "-Comment="; protected FlexoEditor EDITOR = new DefaultFlexoEditor(null) { @Override public boolean performResourceScanning() { return false; }; }; protected FlexoCVSConsoleListener cvsConsole; private boolean isFirstCommit = false; protected File projectDirectory = null; protected org.openflexo.fps.CVSRepository cvsRepository; private boolean override = false; protected String moduleName; protected String tag; protected String comment; protected CVSRepositoryList repositories; private Vector<CVSFile> files; private Vector<FPSObject> filesToCommit; private Vector<FPSObject> conflictingMergeableFiles; private Vector<CVSFile> conflictingFiles; private Vector<FPSObject> filesToUpdate; @Override protected String getName() { return "Project merger"; } public static void main(String[] args) { launch(FlexoProjectMergeMain.class, args); } @Override protected void init(String[] args) throws MissingArgumentException { super.init(args); File repository = null; if (args.length > 0) { for (int i = 0; i < args.length; i++) { if (args[i].equals(FIRST_COMMIT_ARGUMENT)) { isFirstCommit = true; } else if (args[i].startsWith(PROJECT_DIRECTORY_ARGUMENT_PREFIX)) { projectDirectory = new File(args[i].substring(PROJECT_DIRECTORY_ARGUMENT_PREFIX.length())); if (!projectDirectory.exists()) { projectDirectory = null; } } else if (args[i].startsWith(CVS_REPOSITORY_FILE_ARGUMENT_PREFIX)) { repository = new File(args[i].substring(CVS_REPOSITORY_FILE_ARGUMENT_PREFIX.length())); if (!repository.exists()) { repository = null; } } else if (args[i].startsWith(OVERRIDE_ARGUMENT)) { override = true; } else if (args[i].startsWith(MODULE_NAME_ARGUMENT_PREFIX)) { moduleName = args[i].substring(MODULE_NAME_ARGUMENT_PREFIX.length()); if (moduleName.startsWith("\"")) { moduleName = moduleName.substring(1); } if (moduleName.endsWith("\"")) { moduleName = moduleName.substring(0, moduleName.length() - 1); } } else if (args[i].startsWith(TAG_ARGUMENT_PREFIX)) { tag = args[i].substring(TAG_ARGUMENT_PREFIX.length()); if (tag.startsWith("\"")) { tag = tag.substring(1); } if (tag.endsWith("\"")) { tag = tag.substring(0, tag.length() - 1); } } else if (args[i].startsWith(COMMENT_ARGUMENT_PREFIX)) { comment = args[i].substring(COMMENT_ARGUMENT_PREFIX.length()); if (comment.startsWith("\"")) { comment = comment.substring(1); } if (comment.endsWith("\"")) { comment = comment.substring(0, comment.length() - 1); } } } } repositories = new CVSRepositoryList(); Properties cvsProperties = new Properties(); try { cvsProperties.loadFromXML(new FileInputStream(repository)); } catch (InvalidPropertiesFormatException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } if (tag != null) { tag = tag.replaceAll("[ \\$,\\.:;@|]", "_"); // Tags cannot contain those special chars. } cvsRepository = new CVSRepository(cvsProperties); repositories.addToCVSRepositories(cvsRepository); cvsConsole = new FlexoCVSConsoleListener(); if (moduleName == null || repository == null) { StringBuilder sb = new StringBuilder(); if (args.length > 0) { for (int i = 0; i < args.length; i++) { sb.append(i).append(": ").append(args[i]).append("\n"); } } if (logger.isLoggable(Level.SEVERE)) { logger.severe("Missing argument. Usage java " + FlexoProjectMergeMain.class.getName() + " " + RESOURCE_PATH_ARGUMENT_PREFIX + " " + CVS_REPOSITORY_FILE_ARGUMENT_PREFIX + " " + PROJECT_DIRECTORY_ARGUMENT_PREFIX + " " + TAG_ARGUMENT_PREFIX + " " + "\n" + (args.length > 0 ? sb.toString() : "No arguments !!!")); } if (moduleName == null) { throw new MissingArgumentException(MODULE_NAME_ARGUMENT_PREFIX); } else { throw new MissingArgumentException(CVS_REPOSITORY_FILE_ARGUMENT_PREFIX); } } } @Override protected void doRun() { CVSFile.xmlDiff3MergeEnabled = true; SharedProject project = null; if (isFirstCommit) { project = shareProject(); checkProjectIsOpenable(); } else { project = SharedProject.openProject(repositories, projectDirectory, cvsRepository, EDITOR); if (project == null) { setExitCodeCleanUpAndExit(PROJECT_NOT_FOUND); } synchronizeProject(project); if (override) { overrideConflictingFiles(); } else { // Here conflicting files contains the files that cannot be // automatically merged if (conflictingFiles.size() > 0) { prepareMergeFailed(); } if (logger.isLoggable(Level.INFO)) { logger.info("Ready to commit " + (filesToCommit.size() + conflictingMergeableFiles.size()) + " files including " + conflictingMergeableFiles.size() + " automatically merged."); } if (conflictingMergeableFiles.size() > 0) { mergeFiles(); } } updateProject(); checkProjectIsOpenable(); commitProject(); } setExitCodeCleanUpAndExit(0); } /** * */ protected void commitProject() { if (logger.isLoggable(Level.INFO)) { logger.info("Starting to commit " + filesToCommit.size() + " files for " + moduleName); } CommitFiles commit = CommitFiles.actionType.makeNewAction(null, filesToCommit, EDITOR); commit.setCommitMessage(comment != null ? comment + "\n" : ""); commit.doAction(); if (!commit.hasActionExecutionSucceeded()) { handleActionFailed(commit, projectDirectory); } if (logger.isLoggable(Level.INFO)) { logger.info("Commit done for " + moduleName); } } /** * */ private void updateProject() { // at this point : all files that were trivially mergeable are merged on disk and mark as merged in CVS // at this point : filesToCommit contains all files that were only locally modified + all files that where trivially mergeable (and // physically merged) // at this point : filesToUpdate contains all files that where only remotely modified. if (filesToUpdate.size() > 0) { if (logger.isLoggable(Level.INFO)) { logger.info("It seems we are ready to commit, but there is some files to update."); } // now : what we want to do is to update all remotely modified files (i.e. : filesToUpdate) and check that the project can be // opened. UpdateFiles updateFiles = UpdateFiles.actionType.makeNewAction(null, filesToUpdate, EDITOR); updateFiles.doAction(); if (!updateFiles.hasActionExecutionSucceeded()) { handleActionFailed(updateFiles, projectDirectory); } } } /** * @throws MergeFailedException */ private void prepareMergeFailed() { if (logger.isLoggable(Level.INFO)) { logger.info("Merge cannot be performed: " + conflictingFiles.size() + " are conflicting. Set logger named " + logger.getName() + " level to FINE to see files and to FINEST to see merge detail."); } if (logger.isLoggable(Level.FINE)) { logger.fine("Conflicting files:"); for (CVSFile file : conflictingFiles) { logger.fine(file.getFileName()); if (logger.isLoggable(Level.FINEST)) { logger.finest(file.getMerge().toString()); } } } File projectCopyDirectory = null; FlexoEditor editor = null; try { List<FlexoStorageResource<? extends StorageResourceData>> conflictingResources = new ArrayList<FlexoStorageResource<? extends StorageResourceData>>(); projectCopyDirectory = FileUtils.createTempDirectory("Copy_", "_of_" + projectDirectory.getName()); FileUtils.copyContentDirToDir(projectDirectory, projectCopyDirectory); editor = FlexoResourceManager.initializeExistingProject(projectCopyDirectory, new FlexoEditorFactory() { @Override public FlexoEditor makeFlexoEditor(FlexoProject project) { return new FlexoBuilderEditor(null, project); } }, null); for (CVSFile file : conflictingFiles) { String fileName = file.getFile().getAbsolutePath(); for (FlexoStorageResource<? extends StorageResourceData> r : editor.getProject().getStorageResources()) { if (fileName.endsWith(r.getResourceFile().getRelativePath())) { conflictingResources.add(r); break; } } } writeToConsole(FlexoBuilderListener.CONFLICTING_RESSOURCES_START_TAG); for (FlexoStorageResource<? extends StorageResourceData> r : conflictingResources) { writeToConsole(r.getResourceType().getLocalizedName() + " " + r.getName() + " is in conflict"); } writeToConsole(FlexoBuilderListener.CONFLICTING_RESSOURCES_END_TAG); } catch (Exception e) { e.printStackTrace(); } finally { if (editor != null && editor.getProject() != null) { editor.getProject().close(); } } setExitCodeCleanUpAndExit(CONFLICTING_FILES); } /** * @throws MergeFailedException */ private void mergeFiles() { if (conflictingMergeableFiles.size() > 0) { MarkAsMergedFiles markAsMerged = MarkAsMergedFiles.actionType.makeNewAction(null, conflictingMergeableFiles, EDITOR); markAsMerged.doAction(); if (!markAsMerged.hasActionExecutionSucceeded()) { handleActionFailed(markAsMerged, projectDirectory); } // now : all conflictingMergeableFiles are physically merge on disk // see : CVSFile.markAsMerged() for (FPSObject file : conflictingMergeableFiles) { // check that all files "mark as merged" aren't conflicting any more if (((CVSFile) file).getStatus().isConflicting()) { conflictingFiles.add((CVSFile) file); // a file that has been "mark as merged" is still conflicting... that's very bad } } if (conflictingFiles.size() > 0) { prepareMergeFailed(); } filesToCommit.addAll(conflictingMergeableFiles); } } /** * */ protected void overrideConflictingFiles() { if (filesToUpdate.size() > 0) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Something is not right. Project was supposed to be locked but there are " + filesToUpdate.size() + " files to update.\n" + "\tI will continue but checked-out project may be different from uploaded project"); } } // If we don't care aboute conflicts and other problems Vector<FPSObject> filesToOverride = new Vector<FPSObject>(); filesToOverride.addAll(conflictingMergeableFiles); filesToOverride.addAll(conflictingFiles); OverrideAndCommitFiles overrideAndCommit = OverrideAndCommitFiles.actionType.makeNewAction(null, filesToOverride, EDITOR); overrideAndCommit.doAction(); if (!overrideAndCommit.hasActionExecutionSucceeded()) { handleActionFailed(overrideAndCommit, projectDirectory); } } /** * @param project */ protected void synchronizeProject(SharedProject project) { // First we synchronize the files with CVS--> retrieves the // status // and the merges if (logger.isLoggable(Level.INFO)) { logger.info("Launching project synchronization for " + moduleName); } SynchronizeWithRepository synchronize = SynchronizeWithRepository.actionType .makeNewAction(project, new Vector<FPSObject>(), EDITOR); synchronize.doAction(); if (!synchronize.hasActionExecutionSucceeded()) { handleActionFailed(synchronize, projectDirectory); } files = project.getAllCVSFiles(); filesToCommit = new Vector<FPSObject>(); conflictingMergeableFiles = new Vector<FPSObject>(); conflictingFiles = new Vector<CVSFile>(); filesToUpdate = new Vector<FPSObject>(); for (CVSFile file : files) { if (file.getStatus().isConflicting()) { if (file.getMerge().isResolved()) { conflictingMergeableFiles.add(file); } else { if (file.getFileName().endsWith(".rmxml.ts") || file.getFileName().endsWith(".autosave")) { continue; } conflictingFiles.add(file); } } if (file.getStatus().isLocallyModified()) { filesToCommit.add(file); } if (file.getStatus().isRemotelyModified()) { filesToUpdate.add(file); } } if (logger.isLoggable(Level.INFO)) { logger.info("Synchronization done for " + moduleName + " with " + filesToCommit.size() + " file to commit, " + conflictingMergeableFiles.size() + " conflicting mergeable files and " + conflictingFiles.size() + " really conflicting files."); } } /** * @throws ProjectNotFoundException * @throws CorruptedProjectException */ protected void checkProjectIsOpenable() { // at this point all files to update are updated. // so we can try to load the project... and see if it's going right !!! File projectCopyDirectory = null; try { projectCopyDirectory = FileUtils.createTempDirectory("Copy_", "_of_" + projectDirectory.getName()); FileUtils.copyContentDirToDir(projectDirectory, projectCopyDirectory); } catch (IOException e1) { e1.printStackTrace(); setExitCodeCleanUpAndExit(PROJECT_NOT_FOUND); } FlexoEditor editor = null; try { editor = FlexoResourceManager.initializeExistingProject(projectCopyDirectory, new FlexoEditorFactory() { @Override public FlexoEditor makeFlexoEditor(FlexoProject project) { return new FlexoBuilderEditor(null, project); } }, null); if (editor == null) { setExitCodeCleanUpAndExit(CORRUPTED_PROJECT_EXCEPTION); } if (editor.getProject() == null) { setExitCodeCleanUpAndExit(CORRUPTED_PROJECT_EXCEPTION); } // printTOCRepositories(editor); } catch (ProjectLoadingCancelledException e) { setExitCodeCleanUpAndExit(CORRUPTED_PROJECT_EXCEPTION); } catch (ProjectInitializerException e) { setExitCodeCleanUpAndExit(CORRUPTED_PROJECT_EXCEPTION); } finally { if (editor != null && editor.getProject() != null) { editor.getProject().close(); } } } /* private void printTOCRepositories(FlexoEditor editor) throws ProjectInitializerException{ if(editor.getProject().getTOCData()!=null){ try { String encodedTOCData = XMLCoder.encodeObjectWithMapping(editor.getProject().getTOCData(), new XMLMapping(new FileResource("Models/TOCModel/short_toc_model_0.1.xml")),StringEncoder.getDefaultInstance()); System.err.println(FlexoBuilderListener.TOCS_START_TAG); System.err.println(encodedTOCData); System.err.println(FlexoBuilderListener.TOCS_END_TAG); } catch (InvalidModelException e) { e.printStackTrace(); throw new ProjectInitializerException(e); } catch (IOException e) { e.printStackTrace(); throw new ProjectInitializerException(e); } catch (SAXException e) { e.printStackTrace(); throw new ProjectInitializerException(e); } catch (ParserConfigurationException e) { e.printStackTrace(); throw new ProjectInitializerException(e); } catch (InvalidObjectSpecificationException e) { e.printStackTrace(); throw new ProjectInitializerException(e); } catch (AccessorInvocationException e) { e.printStackTrace(); throw new ProjectInitializerException(e); } catch (DuplicateSerializationIdentifierException e) { e.printStackTrace(); throw new ProjectInitializerException(e); } } } */ /** * */ private SharedProject shareProject() { // If it is the first commit, then we start by sharing. ShareProject shareProject = ShareProject.actionType.makeNewAction(repositories, null, EDITOR); shareProject.setProjectDirectory(projectDirectory); shareProject.setRepository(cvsRepository); shareProject.setCvsIgnorize(true); shareProject.setRemoveExistingCVSDirectories(true); shareProject.setModuleName(moduleName); shareProject.setVendorTag(tag); shareProject.doAction(); if (!shareProject.hasActionExecutionSucceeded()) { handleActionFailed(shareProject, projectDirectory); } else if (logger.isLoggable(Level.INFO)) { logger.info("Project sharing succeeded"); } return shareProject.getProject(); } @Override protected void cleanUp() { if (getExitCode() != 0) { reportMessage(cvsConsole.getLogs().toString()); } super.cleanUp(); } }