Author: bpoussin Date: 2008-08-20 13:06:19 +0000 (Wed, 20 Aug 2008) New Revision: 1306 Added: trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSActionEvent.java trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VetoableActionListener.java Modified: trunk/isis-fish/src/java/fr/ifremer/isisfish/IsisFish.java trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/AbstractVCS.java trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCS.java trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSNone.java trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSSVN.java trunk/isis-fish/src/test/fr/ifremer/isisfish/vcs/VCSSVNTest.java Log: tout est code, et teste en test unitaire, y'a plus qu'a tester en grandeur nature (vcs, option) Modified: trunk/isis-fish/src/java/fr/ifremer/isisfish/IsisFish.java =================================================================== --- trunk/isis-fish/src/java/fr/ifremer/isisfish/IsisFish.java 2008-08-19 18:37:13 UTC (rev 1305) +++ trunk/isis-fish/src/java/fr/ifremer/isisfish/IsisFish.java 2008-08-20 13:06:19 UTC (rev 1306) @@ -52,10 +52,14 @@ import fr.ifremer.isisfish.util.StringConverter; import fr.ifremer.isisfish.util.TimeUnitConverter; import fr.ifremer.isisfish.vcs.VCS; +import fr.ifremer.isisfish.vcs.VCSActionEvent; import fr.ifremer.isisfish.vcs.VCSException; import fr.ifremer.isisfish.vcs.VCSFactory; +import fr.ifremer.isisfish.vcs.VCSNone; +import fr.ifremer.isisfish.vcs.VetoableActionListener; import java.io.File; import java.text.SimpleDateFormat; +import java.util.Arrays; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.logging.LogFactory; import static org.codelutin.i18n.I18nf._; @@ -63,6 +67,7 @@ import org.codelutin.math.matrix.DoubleBigVector; import org.codelutin.math.matrix.MatrixFactory; import java.util.Locale; +import javax.swing.JOptionPane; import org.apache.commons.logging.Log; import org.swixat.SwiXAT; import org.swixat.model.Context; @@ -197,6 +202,49 @@ // // } + static class VCSActionAsker implements VetoableActionListener { + + public boolean canDoAction(VCS vcs, VCSActionEvent action, File... files) { + boolean result = true; + if (action == VCSActionEvent.SWITCH_PROTOCOL) { + result = ask(_("Protocol to access repository script has changed.\n" + + "Do you want to switch your repository ?")); + if (!result) { + // l'utilisateur ne souhaite pas changer de protocol, + // on force le repo en read-only pour eviter les erreurs + vcs.setWriteable(false); + } + } else if (action == VCSActionEvent.SWITCH) { + result = ask(_("You don't use correction repository script for" + + " your application version %s.\nDo you want to switch" + + " your repository ?", IsisConfig.getVersion())); + if (!result) { + // l'utilisateur ne souhaite pas changer de branche, + // on force le repo en read-only pour eviter les erreurs + vcs.setWriteable(false); + } + } else if (action == VCSActionEvent.UPDATE_REPOSITORY) { + result = ask(_("Your repository is not up to date.\n" + + "Do you want to update your repository ?\n\n" + + "Remote modified file is\n%s", Arrays.toString(files))); + } + return result; + } + + } + + public static boolean ask(String msg) { + boolean result = true; + int value = JOptionPane.showConfirmDialog(null, msg); + result = value == JOptionPane.OK_OPTION; + return result; + } + + protected static void switchToNoneVCS() { + config.setOption(IsisConfig.Option.VCS_PROTOCOLE.key, "none"); + config.saveForUser(); + vcs = VCSFactory.createVCS(config); + } /** * Initialise le VCSNone et check s'il y a des mises a jour pour prevenir * l'utilisateur @@ -204,49 +252,67 @@ static public void initVCS() throws VCSException { // init vcs vcs = VCSFactory.createVCS(config); + VCSActionAsker asker = new VCSActionAsker(); + vcs.addVetoableActionListener(asker); // Si le repo local exist mais n'est pas du bon type, on renome ce repertoire File local = config.getDatabaseDirectory(); if (local.exists()) { if(!vcs.isValidLocalRepository()) { - File localBackup = new File(local.getParentFile(), - local.getName() + "-" + - new SimpleDateFormat().format(new Date())); - if (!local.renameTo(localBackup)) { - throw new IsisFishRuntimeException( - "Can rename local repository that don't use svn"); + if (ask(_("Your database repository don't use correct protocol.\nDo you want to make backup of your database and take the correct one ?"))) { + File localBackup = new File(local.getParentFile(), + local.getName() + "-" + + new SimpleDateFormat().format(new Date())); + if (!local.renameTo(localBackup)) { + throw new IsisFishRuntimeException( + "Can't rename local repository that don't use svn"); + } + } else { + switchToNoneVCS(); } } } // Si le repo local n'existe pas on fait un check out complet if (!local.exists()) { - // Si on utilise pas le bon tag on change de tag - VersionNumber tag = IsisConfig.getApiVersion(); - if (!vcs.isTag(tag)) { - // pas de tag pour cette version, on checkout le trunk - tag = null; - } + if (vcs instanceof VCSNone) { + JOptionPane.showMessageDialog(null, _( + "No database found and can't get it.\n" + + "You must go to ISIS-Fish web site and download database manualy."), + _("Database needed %s", IsisConfig.getVersion()), + JOptionPane.WARNING_MESSAGE); + } else { + // Si on utilise pas le bon tag on change de tag + VersionNumber tag = IsisConfig.getApiVersion(); + if (!vcs.isTag(tag)) { + // pas de tag pour cette version, on checkout le trunk + tag = null; + } - vcs.checkout(tag, false); + // initialise le repo local + vcs.checkout(tag, false); - AnalysePlanStorage.checkout(); - ExportStorage.checkout(); - FormuleStorage.checkout(); - RuleStorage.checkout(); - ScriptStorage.checkout(); - SimulatorStorage.checkout(); + // ajoute les repertoires qu'il faut + AnalysePlanStorage.checkout(); + ExportStorage.checkout(); + FormuleStorage.checkout(); + RuleStorage.checkout(); + ScriptStorage.checkout(); + SimulatorStorage.checkout(); - vcs.update(new File(local, SimulationStorage.SIMULATION_PATH), false); - vcs.update(new File(local, RegionStorage.REGION_PATH), false); - try { - RegionStorage.checkout("DemoRegion"); - } catch (TopiaException eee) { - log.warn("Can't checkout DemoRegion", eee); + // on ne prend pas toutes les simu ni toutes les regions + vcs.update(new File(local, SimulationStorage.SIMULATION_PATH), false); + vcs.update(new File(local, RegionStorage.REGION_PATH), false); + try { + RegionStorage.checkout("DemoRegion"); + } catch (TopiaException eee) { + log.warn("Can't checkout DemoRegion", eee); + } } } if (!local.exists()) { + // arrive ici le repo devrait exister throw new IsisFishRuntimeException("Can't find local repository"); } @@ -274,6 +340,7 @@ // check file status vcs.checkFileStatus(); + } /** Modified: trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/AbstractVCS.java =================================================================== --- trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/AbstractVCS.java 2008-08-19 18:37:13 UTC (rev 1305) +++ trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/AbstractVCS.java 2008-08-20 13:06:19 UTC (rev 1306) @@ -20,24 +20,11 @@ package fr.ifremer.isisfish.vcs; -import fr.ifremer.isisfish.IsisConfig; -import fr.ifremer.isisfish.IsisFish; -import fr.ifremer.isisfish.IsisFishRuntimeException; -import fr.ifremer.isisfish.datastore.AnalysePlanStorage; -import fr.ifremer.isisfish.datastore.ExportStorage; -import fr.ifremer.isisfish.datastore.FormuleStorage; -import fr.ifremer.isisfish.datastore.RegionStorage; -import fr.ifremer.isisfish.datastore.RuleStorage; -import fr.ifremer.isisfish.datastore.ScriptStorage; -import fr.ifremer.isisfish.datastore.SimulationStorage; -import fr.ifremer.isisfish.datastore.SimulatorStorage; import java.io.File; -import java.text.SimpleDateFormat; -import java.util.Date; +import java.util.HashSet; +import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.codelutin.topia.TopiaException; -import org.codelutin.util.VersionNumber; /** * @@ -52,6 +39,7 @@ /** to use log facility, just put in your code: log.info(\"...\"); */ static private Log log = LogFactory.getLog(AbstractVCS.class); + protected Set<VetoableActionListener> listeners = new HashSet<VetoableActionListener>(); protected File localRepository; protected String protocol; protected String host; @@ -59,6 +47,7 @@ protected File sshKeyFile; protected String login; protected String password; + protected boolean writeable = true; public AbstractVCS(File localRepository, String protocol, String host, String path, @@ -72,6 +61,39 @@ this.password = password; } + public void addVetoableActionListener(VetoableActionListener l) { + listeners.add(l); + } + + public void remoteVetoableActionListener(VetoableActionListener l) { + listeners.remove(l); + } + + public void setWriteable(boolean value) { + this.writeable = value; + } + + protected boolean fireAction(VCSActionEvent e, File ... files) { + boolean result = true; + if (listeners.size() > 0) { + // on evite la concurrence + VetoableActionListener[] ls = + listeners.toArray(new VetoableActionListener[listeners.size()]); + for (VetoableActionListener l : ls) { + result = result && l.canDoAction(this, e, files); + if (!result) { + // on s'arrete des qu'il y en a un qui ne souhaite pas + // qu'on fasse l'action + break; + } + } + } + if (!result) { + log.info("Canceled action: " + e); + } + return result; + } + public File getLocalRepository() { return localRepository; } @@ -156,22 +178,9 @@ String filename = file.getName(); return !".svn".equals(filename) && !"CVS".equals(filename) && !filename.endsWith("~") && - // si le fichier n'appartient pas a loca repository, il ne pourra pas etre versionne + // si le fichier n'appartient pas a loca repository, + // il ne pourra pas etre versionne file.getAbsolutePath().startsWith(getLocalRepository().getAbsolutePath()); } - /** - * Verifie si tous les fichiers du repository local sont les dernieres - * version par rapport au serveur. Si ce n'est pas le cas et que l'on est - * en mode interactif (mode graphique), on lui propose de mettre a jour - * les fichiers, avec la possibilite de voir les changements sur les - * fichiers - */ - public void checkFileStatus() throws VCSException { - if (isConnected()) { - // si des fichiers ont ete mis a jour sur le serveur on se synchronise - // FIXME a faire - } - } - } Modified: trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCS.java =================================================================== --- trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCS.java 2008-08-19 18:37:13 UTC (rev 1305) +++ trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCS.java 2008-08-20 13:06:19 UTC (rev 1306) @@ -67,6 +67,18 @@ public static final String VCS_USER_PASSWORD = "vcs.password"; /** + * Ajout un listener pouvant interdire les actions du vcs + * @param l + */ + public void addVetoableActionListener(VetoableActionListener l); + + /** + * Supprime un listener pouvant interdire les actions du vcs + * @param l + */ + public void remoteVetoableActionListener(VetoableActionListener l); + + /** * Get local repository directory */ public File getLocalRepository(); @@ -83,24 +95,14 @@ * @return */ public boolean isWriteable() throws VCSException; + + /** + * Permit to force repository to read-only if value is false, otherwize + * use normal rules. + * @param value + */ + public void setWriteable(boolean value); -// /** -// * Get the server address of the local file -// * ex: svn+ssh://bpoussin at labs.libre-entreprise.org/svnroot/isis-fish/trunk/isis-fish/pom.xml -// * -// * @param f file that we want to know the source -// * @return -// */ -// public String getLocalRepositorySource(File f) throws VCSException; -// -// /** -// * Get the root server address of the local repository -// * ex: svn+ssh://bpoussin at labs.libre-entreprise.org/svnroot/isis-fish -// * -// * @return -// */ -// public String getLocalRepositoryRoot() throws VCSException; - /** * Commit specified files, if files is null, all files are commited * @param files files to commit @@ -125,14 +127,6 @@ */ public void checkout(VersionNumber tag, boolean recurse) throws VCSException; -// /** -// * switch to new protocole, can be used to switch between anonymous and -// * authenticate protocole -// * @param protocoleType -// * @throws fr.ifremer.isisfish.vcs.VCSException -// */ -// public void switchProtocole(String protocoleType) throws VCSException; - /** * Delete and commit files in server repository * @param files file to delete @@ -223,7 +217,7 @@ * @return true if there are some merging conflict, false otherwize * @throws fr.ifremer.isisfish.vcs.VCSException */ - public boolean update(File file, boolean recurse) throws VCSException; + public List<File> update(File file, boolean recurse) throws VCSException; /** * Verifie la connexion et si le protocole a change, switch le repository Added: trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSActionEvent.java =================================================================== --- trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSActionEvent.java (rev 0) +++ trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSActionEvent.java 2008-08-20 13:06:19 UTC (rev 1306) @@ -0,0 +1,42 @@ +/* *##% + * Copyright (C) 2002-2008 Code Lutin, Benjamin Poussin + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + *##%*/ + +package fr.ifremer.isisfish.vcs; + +/** + * Represente une action VCS que l'on souhaite faire + * + * @author poussin + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +public enum VCSActionEvent { + + CHECKOUT, // when checkout + COMMIT, // when commit + UPDATE, // when update + UPDATE_REPOSITORY, // when checkFileStatus + SWITCH, // when setTag + SWITCH_PROTOCOL, // when checkProtocol + DELETE, // when delete + ADD // when add + +} Modified: trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSNone.java =================================================================== --- trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSNone.java 2008-08-19 18:37:13 UTC (rev 1305) +++ trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSNone.java 2008-08-20 13:06:19 UTC (rev 1306) @@ -19,7 +19,6 @@ package fr.ifremer.isisfish.vcs; -import fr.ifremer.isisfish.IsisConfig; import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -181,26 +180,18 @@ * @return true if there are some merging conflict, false otherwize * @throws fr.ifremer.isisfish.vcs.VCSException */ - public boolean update(File file, boolean recurse) throws VCSException { + public List<File> update(File file, boolean recurse) throws VCSException { throw new VCSException("Can't update file with dummy VCS"); } -// public String getLocalRepositorySource(File f) throws VCSException { -// throw new VCSException("Not supported."); -// } -// -// public String getLocalRepositoryRoot() throws VCSException { -// throw new VCSException("Not supported."); -// } -// -// public String getLocalRepositoryTag() throws VCSException { -// throw new VCSException("Not supported."); -// } - public void checkProtocol() throws VCSException { // nothing to do } + public void checkFileStatus() throws VCSException { + // nothing to do + } + public boolean isWriteable() throws VCSException { return false; } Modified: trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSSVN.java =================================================================== --- trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSSVN.java 2008-08-19 18:37:13 UTC (rev 1305) +++ trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VCSSVN.java 2008-08-20 13:06:19 UTC (rev 1306) @@ -22,9 +22,14 @@ import static org.codelutin.i18n.I18nf._; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codelutin.util.VersionNumber; @@ -36,9 +41,12 @@ import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory; import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl; import org.tmatesoft.svn.core.wc.ISVNOptions; +import org.tmatesoft.svn.core.wc.ISVNStatusHandler; import org.tmatesoft.svn.core.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNStatus; +import org.tmatesoft.svn.core.wc.SVNStatusType; import org.tmatesoft.svn.core.wc.SVNWCUtil; /** @@ -128,11 +136,13 @@ } if (mustSwitch) { - String path = url.getPath(); - int port = url.getPort(); - SVNURL newUrl = SVNURL.create(getProtocol(), getLogin(), - getHost(), port, path, true); - getSVNManager().getUpdateClient().doRelocate(localRoot, url, newUrl, true); + if (fireAction(VCSActionEvent.SWITCH_PROTOCOL)) { + String path = url.getPath(); + int port = url.getPort(); + SVNURL newUrl = SVNURL.create(getProtocol(), getLogin(), + getHost(), port, path, true); + getSVNManager().getUpdateClient().doRelocate(localRoot, url, newUrl, true); + } } } catch (SVNException eee) { throw new VCSException(_("Can't get address on serveur of local repository"), eee); @@ -140,37 +150,31 @@ } - - + /** + * Verifie si tous les fichiers du repository local sont les dernieres + * version par rapport au serveur. Si ce n'est pas le cas et que l'on est + * en mode interactif (mode graphique), on lui propose de mettre a jour + * les fichiers, avec la possibilite de voir les changements sur les + * fichiers + */ + public void checkFileStatus() throws VCSException { + if (isConnected()) { + Map<File, SVNStatus> status = getRemoteStatus(null, true); + // si des fichiers ont ete mis a jour sur le serveur on se synchronise + if (status.size() > 0) { + if (fireAction(VCSActionEvent.UPDATE_REPOSITORY, + status.keySet().toArray(new File[status.size()]))) { + update(null, true); + } + } + } + } + protected SVNURL getRemoteURL() throws SVNException { SVNURL remoteURL = SVNURL.parseURIEncoded(getRemoteRepository()); return remoteURL; } - -// public String getLocalRepositoryRoot() throws VCSException { -// try { -// File localRoot = getLocalRepository(); -// SVNInfo info = getSVNManager().getWCClient().doInfo(localRoot, SVNRevision.WORKING); -// SVNURL url = info.getCopyFromURL(); -// String result = url.toDecodedString(); -// return result; -// } catch (SVNException eee) { -// throw new VCSException(_("Can't get address on serveur of local repository"), eee); -// } -// } -// -// public String getLocalRepositorySource(File f) throws VCSException { -// try { -// // TODO perhaps, check if file is in getLocalRepository() file ? -// SVNInfo info = getSVNManager().getWCClient().doInfo(f, SVNRevision.WORKING); -// SVNURL url = info.getURL(); -// String result = url.toDecodedString(); -// return result; -// } catch (SVNException eee) { -// throw new VCSException(_("Can't get address on serveur of local repository"), eee); -// } -// } - + /** * Retourne l'url du repository distant * ex: ssh+svn://labs.le.org/svnroot/isis-fish/data/branches/3.2 @@ -206,10 +210,19 @@ } public void commit(List<File> files, String msg) throws VCSException { + if (!isWriteable()) { + throw new VCSException("You can't commit file, this repository is readonly"); + } + if (files == null) { + files = Arrays.asList(getLocalRepository()); + } + if (fireAction(VCSActionEvent.COMMIT, files.toArray(new File[files.size()]))) { + commitWithoutCheck(files, msg); + } + } + + protected void commitWithoutCheck(List<File> files, String msg) throws VCSException { try { - if (files == null) { - files = Arrays.asList(getLocalRepository()); - } SVNCommitInfo commitInfo = getSVNManager().getCommitClient() .doCommit(files.toArray(new File[files.size()]), false, // keep lock @@ -220,20 +233,25 @@ } catch (SVNException eee) { throw new VCSException("Can't commit files", eee); } - } + } public void add(List<File> files, String msg) throws VCSException { + if (!isWriteable()) { + throw new VCSException("You can't add file, this repository is readonly"); + } try { - for (File file : files) { - // FIXME ajoute dans le ignore les fichiers regions/<region>/data/* - getSVNManager().getWCClient().doAdd(file, - true, // force add to allready added file (no error) - false, // don't create dir if not exist - true, // add parent dir if is not versionned - false, // no recurse - false); // don't add ignore file + if (fireAction(VCSActionEvent.ADD, files.toArray(new File[files.size()]))) { + for (File file : files) { + // FIXME ajoute dans le ignore les fichiers regions/<region>/data/* + getSVNManager().getWCClient().doAdd(file, + true, // force add to allready added file (no error) + false, // don't create dir if not exist + true, // add parent dir if is not versionned + false, // no recurse + false); // don't add ignore file + } + commitWithoutCheck(files, msg); } - commit(files, msg); } catch (SVNException eee) { throw new VCSException("Can't add file", eee); } @@ -241,35 +259,147 @@ public void checkout(VersionNumber tag, boolean recurse) throws VCSException { try { - String tagPath = "/trunk/"; - if (tag != null) { - tagPath = "/tags/" + tag + "/"; - } + if (fireAction(VCSActionEvent.CHECKOUT, getLocalRepository())) { + String tagPath = "/trunk/"; + if (tag != null) { + tagPath = "/tags/" + tag + "/"; + } - SVNURL source = getRemoteURL().appendPath(tagPath, false); + SVNURL source = getRemoteURL().appendPath(tagPath, false); - File destDir = getLocalRepository(); - destDir.mkdirs(); - - getSVNManager().getUpdateClient().doCheckout(source, destDir, null, - SVNRevision.HEAD, recurse); + File destDir = getLocalRepository(); + destDir.mkdirs(); + + getSVNManager().getUpdateClient().doCheckout(source, destDir, null, + SVNRevision.HEAD, recurse); + } } catch (SVNException eee) { throw new VCSException(_("Can't checkout"), eee); } } public void delete(List<File> files, String msg) throws VCSException { - try { - for (File file : files) { - // FIXME check argument in documentation - getSVNManager().getWCClient().doDelete(file, true, msg != null, false); + if (!isWriteable()) { + throw new VCSException("You can't delete file, this repository is readonly"); + } + try { + if (fireAction(VCSActionEvent.DELETE, files.toArray(new File[files.size()]))) { + for (File file : files) { + getSVNManager().getWCClient().doDelete(file, + true, // force la deletion + true, // delete local file too + false); // is not just a test + } + commitWithoutCheck(files, msg); } - commit(files, msg); } catch (SVNException eee) { throw new VCSException("Can't add file", eee); } } +// protected String statusToString(SVNStatus status) { +// SVNStatusType type = status.getRemoteContentsStatus(); +// +// String result = "'" + type.getCode() + "' '" + type.getID() + "' " + type.toString(); +// type = status.getContentsStatus(); +// result += "|'" + type.getCode() + "' '" + type.getID() + "' " + type.toString(); +// +// return result; +// } + + /** + * Recherhce le status des fichiers locaux, ne retourne jamais les fichiers + * NOMAL ou NONE sauf si demande explicitement via wanted + * + * @param file le repertoire a partir duquel on souhaite le status + * @param recurse si l'on souhaite le faire recursivement + * @param wanted l'ensemble des status type que l'on recheche, si vide + * recherche tous les statuts + * @return une map avec comme cle le File local et en valeur le status + * @throws fr.ifremer.isisfish.vcs.VCSException + */ + protected Map<File, SVNStatus> getLocalStatus(File file, boolean recurse, SVNStatusType ... wanted) throws VCSException { + try { + if (file == null) { + file = getLocalRepository(); + } + + final Map<File, SVNStatus> result = new HashMap<File, SVNStatus>(); + final Set<SVNStatusType> acceptedStatusType = + new HashSet<SVNStatusType>(Arrays.asList(wanted)); + + ISVNStatusHandler handler = new ISVNStatusHandler() { + public void handleStatus(SVNStatus status) throws SVNException { + if ((acceptedStatusType.size() == 0 && + status.getContentsStatus() != SVNStatusType.STATUS_NONE && + status.getContentsStatus() != SVNStatusType.STATUS_NORMAL) || + acceptedStatusType.contains(status.getContentsStatus())) { + File statusFile = status.getFile(); + if (statusFile.exists() && (!statusFile.isDirectory() || + status.getRemoteContentsStatus() == SVNStatusType.STATUS_ADDED || + status.getRemoteContentsStatus() == SVNStatusType.STATUS_DELETED)) { + // on ne met pas les repertoires pere dans le status + // car en fait ca veut dire qu'un fichier/rep dans ce + // repertoire a ete ajout/modifier/delete, et on l'aura + // aussi dans les resultats et ca suffit + result.put(statusFile, status); + } + } + } + }; + getSVNManager().getStatusClient().doStatus(file, + recurse, + false, // on remote + !acceptedStatusType.contains(SVNStatusType.STATUS_NORMAL), // report only change file, + acceptedStatusType.contains(SVNStatusType.STATUS_IGNORED), // includeIgnored, + handler); + return result; + } catch (SVNException eee) { + throw new VCSException("Can't add file", eee); + } + } + + protected Map<File, SVNStatus> getRemoteStatus(File file, boolean recurse, SVNStatusType ... wanted) throws VCSException { + try { + if (file == null) { + file = getLocalRepository(); + } + + final Map<File, SVNStatus> result = new HashMap<File, SVNStatus>(); + final Set<SVNStatusType> acceptedStatusType = + new HashSet<SVNStatusType>(Arrays.asList(wanted)); + + ISVNStatusHandler handler = new ISVNStatusHandler() { + public void handleStatus(SVNStatus status) throws SVNException { + if ((acceptedStatusType.size() == 0 && + status.getRemoteContentsStatus() != SVNStatusType.STATUS_NONE && + status.getRemoteContentsStatus() != SVNStatusType.STATUS_NORMAL) || + acceptedStatusType.contains(status.getRemoteContentsStatus())) { + File statusFile = status.getFile(); + if (statusFile.exists() && (!statusFile.isDirectory() || + status.getRemoteContentsStatus() == SVNStatusType.STATUS_ADDED || + status.getRemoteContentsStatus() == SVNStatusType.STATUS_DELETED)) { + // on ne met pas les repertoires pere dans le status + // car en fait ca veut dire qu'un fichier/rep dans ce + // repertoire a ete ajout/modifier/delete, et on l'aura + // aussi dans les resultats et ca suffit + result.put(statusFile, status); + } + } + } + }; + getSVNManager().getStatusClient().doStatus(file, + recurse, + true, // on remote + !acceptedStatusType.contains(SVNStatusType.STATUS_NORMAL), // report only change file, + acceptedStatusType.contains(SVNStatusType.STATUS_IGNORED), // includeIgnored, + handler); + return result; + } catch (SVNException eee) { + throw new VCSException("Can't add file", eee); + } + } + public Map<File, String> getChanglog(List<File> files) { throw new UnsupportedOperationException("Not supported yet."); } @@ -299,31 +429,65 @@ } public boolean isUpToDate(File file) throws VCSException { - throw new UnsupportedOperationException("Not supported yet."); + try { + SVNStatus status = getSVNManager().getStatusClient().doStatus(file, + true); // on remote + boolean result = true; // FIXME status.getContentsStatus(). + return result; + } catch (SVNException eee) { + throw new VCSException("Can't add file", eee); + } } - public boolean update(File file, boolean recurse) throws VCSException { + public List<File> update(File file, boolean recurse) throws VCSException { + List<File> result = new ArrayList<File>(); try { + if (file == null) { + file = getLocalRepository(); + } if (!accept(file)) { throw new VCSException("Can't update file that not in local repository"); } - while (!file.getParentFile().exists()) { - update(file.getParentFile(), false); + if (fireAction(VCSActionEvent.UPDATE, file)) { + // si le repertoire pere, n'est pas encore dans le repo local + // il faut aussi l'ajouter + if (!file.getParentFile().exists()) { + update(file.getParentFile(), false); + } + getSVNManager().getUpdateClient().doUpdate(file, SVNRevision.HEAD, recurse); + + // recherche de tous les fichiers locaux en conflit + Map<File, SVNStatus> status = getLocalStatus(file, recurse, SVNStatusType.STATUS_CONFLICTED); + if (status.size() > 0) { + result.addAll(status.keySet()); + // on supprime les conflits pour pouvoir commiter convenablement + // les fichiers + getSVNManager().getWCClient().doResolve(file, recurse); + } } - getSVNManager().getUpdateClient().doUpdate(file, SVNRevision.HEAD, recurse); } catch (SVNException eee) { throw new VCSException("Can't update files", eee); } - return false; // FIXME rechercher s'il y a eu des conflits dans les fichiers + return result; } public boolean isWriteable() throws VCSException { - String login = getLogin(); - String tag = getTag(); + + // si on est clairement en anonyme + boolean result = writeable; + if (writeable) { + // check normal rules + String login = getLogin(); + result = result && login != null && !"".equals(login) && !"anonymous".equals(login); + // meme s'il n'y a pas d'utilisateur, mais qu'on utilise le protocole file:// on est writeable + result = result || getProtocol().startsWith("file"); + // ou que le repertoire n'est pas utilisable pour ce type de vcs + result = result && isValidLocalRepository(); + // ou que l'on est dans un tag, donc par convention not writeable + result = result && !getTag().startsWith("/tags"); + } - boolean result = login != null && !"".equals(login) && !"anonymous".equals(login); - result = result && !tag.startsWith("/tags"); - + // on indique que l'utilisateur n'a pas le droit d'ecrire return result; } @@ -348,9 +512,15 @@ try { File localRoot = getLocalRepository(); SVNInfo info = getSVNManager().getWCClient().doInfo(localRoot, SVNRevision.WORKING); - String root = info.getCopyFromURL().toDecodedString(); String url = info.getURL().toDecodedString(); - String result = url.substring(root.length()); + String result = "/trunk"; + if (!url.endsWith("/trunk")) { + // on est pas sur le trunk, on est soit sur un tag ou une + // branche, dans les deux cas, il faut descendre de 2 / + int i = url.lastIndexOf("/"); + i = url.lastIndexOf("/", i-1); + result = url.substring(i); + } return result; } catch (SVNException eee) { throw new VCSException(_("Can't get address on serveur of local repository"), eee); @@ -363,13 +533,21 @@ if (version != null) { tag = "/tags/" + version; } + + String currantTag = getTag(); + if (!tag.equals(currantTag)) { + // on ne fait le switch que si le tag change rellement, sinon + // c equivalent a faire un update, ce que l'on ne souhaite pas + // forcement + if (fireAction(VCSActionEvent.SWITCH)) { + File localRoot = getLocalRepository(); - File localRoot = getLocalRepository(); - - SVNURL newUrl = getRemoteURL(); - newUrl = newUrl.appendPath(tag, true); - - getSVNManager().getUpdateClient().doSwitch(localRoot, newUrl, SVNRevision.HEAD, true); + SVNURL newUrl = getRemoteURL(); + newUrl = newUrl.appendPath(tag, true); + + getSVNManager().getUpdateClient().doSwitch(localRoot, newUrl, SVNRevision.HEAD, true); + } + } } catch (SVNException eee) { throw new VCSException(_("Can't get address on serveur of local repository"), eee); } Added: trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VetoableActionListener.java =================================================================== --- trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VetoableActionListener.java (rev 0) +++ trunk/isis-fish/src/java/fr/ifremer/isisfish/vcs/VetoableActionListener.java 2008-08-20 13:06:19 UTC (rev 1306) @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2002-2008 Code Lutin, Benjamin Poussin + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package fr.ifremer.isisfish.vcs; + +import java.io.File; + +/** + * Permet a une action d'etre interdite avant qu'elle ne soit faite. Pour cela + * Il faut implanter cette classe pour surveiller les actions, et l'enregistrer + * en tant que listener d'action sur le vcs. + * + * @author poussin + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +public interface VetoableActionListener { + + /** + * Permet d'arreter l'action qui souhaite etre faite, si on retourne false + * @param vcs le vcs qui va faire l'action + * @param action l'action que l'on souhaite faire + * @param files les fichiers impactes par l'action + * @return vrai si l'action peut-etre faite, false pour interdire l'action + */ + public boolean canDoAction(VCS vcs, VCSActionEvent action, File ... files); + +} Modified: trunk/isis-fish/src/test/fr/ifremer/isisfish/vcs/VCSSVNTest.java =================================================================== --- trunk/isis-fish/src/test/fr/ifremer/isisfish/vcs/VCSSVNTest.java 2008-08-19 18:37:13 UTC (rev 1305) +++ trunk/isis-fish/src/test/fr/ifremer/isisfish/vcs/VCSSVNTest.java 2008-08-20 13:06:19 UTC (rev 1306) @@ -52,6 +52,7 @@ static File template = new File("/tmp/testsvn-template"); static File remoteRepo = new File("/tmp/testsvn-repo"); static File localRepo = new File("/tmp/testsvn-local"); + static File localRepoTrunk = new File("/tmp/testsvn-localTrunk"); static { try { @@ -65,6 +66,9 @@ if (localRepo.exists()) { FileUtil.deleteRecursively(localRepo); } + if (localRepoTrunk.exists()) { + FileUtil.deleteRecursively(localRepoTrunk); + } // creation de l'instance de notre VCS pour les tests // on le fait au debut pour que la l'init de la lib svn soit fait @@ -200,9 +204,21 @@ @Test public void testAll() throws Exception { System.out.println("checkout"); + // trunk contient une copie complete de trunk, elle permet de test + // status lors que l'on fait des commits sur le trunk sur instance + VCSSVN trunk = new VCSSVN( + localRepoTrunk, + "file", + "", + remoteRepo.getAbsolutePath() + "/" + "isis-fish-data", + null, + "", ""); + trunk.checkout(null, true); + // on ne checkout rien juste le .svn dans le repertoire racine instance.checkout(null, false); assertTrue(instance.getLocalRepository().exists()); + assertTrue(instance.getTag().startsWith("/trunk")); // update scripts dir instance.update(new File(instance.getLocalRepository(), "scripts"), true); @@ -224,11 +240,56 @@ instance.delete(Arrays.asList(demo), "suppression d'une region"); assertFalse(demo.exists()); + // ajout d'un fichier sur le trunk avant passage sur le tag + File fileToAdd = new File(instance.getLocalRepository(), "newfile.txt"); + String lecontent = "Le nouveau fichier"; + FileUtil.writeString(fileToAdd, lecontent); + instance.add(Arrays.asList(fileToAdd), "ajout d'un fichier"); + // test switchTag instance.setTag(new VersionNumber(3, 1, 0)); + assertTrue(instance.getTag().startsWith("/tags/3.1.0")); assertEquals(fileContentTag, FileUtil.readAsString(version)); assertTrue(demo.exists()); + // recherche du status des fichiers + File fileVersion = new File(trunk.getLocalRepository(), "scripts" + File.separator + "version.txt"); + File fileDeleted = new File(trunk.getLocalRepository(), "regions" + File.separator + "DemoRegion"); + Map map = trunk.getRemoteStatus(trunk.getLocalRepository(), true); + assertEquals(2, map.size()); // version.txt modifie, DemoRegion supprimee + assertTrue(map.containsKey(fileVersion)); + assertTrue(map.containsKey(fileDeleted)); + + // modif dans repo trunk de version pour qu'il y ait un conflit + FileUtil.writeString(fileVersion, "Le nouveau content de version"); + + // update global du repo trunk + List<File> conflictFile = trunk.update(null, true); + System.out.println("conflictFile: " + conflictFile); + assertEquals(1, conflictFile.size()); + assertTrue(conflictFile.contains(fileVersion)); + + File fileAdded = new File(trunk.getLocalRepository(), "newfile.txt"); + assertEquals(lecontent, FileUtil.readAsString(fileAdded)); + assertFalse(fileDeleted.exists()); + + // recherche du status des fichiers en remote, il ne doit plus y avoir de diff + Map map2 = trunk.getRemoteStatus(trunk.getLocalRepository(), true); + assertEquals(0, map2.size()); + + // il doit toujours y avoir version.txt qui est modifier localement + Map map3 = trunk.getLocalStatus(trunk.getLocalRepository(), true); + assertEquals(1, map3.size()); + assertTrue(map3.containsKey(fileVersion)); + + // on commit le fichier version.txt pour verifier qu'en local et remote + // il n'y a plus de modif + trunk.commit(Arrays.asList(fileVersion), "Commit fichier version, avec les conflits"); + Map map4 = trunk.getRemoteStatus(trunk.getLocalRepository(), true); + assertEquals(0, map4.size()); + Map map5 = trunk.getLocalStatus(trunk.getLocalRepository(), true); + assertEquals(0, map5.size()); + } @@ -240,7 +301,14 @@ System.out.println("add"); List<File> files = new ArrayList<File>(); String msg = ""; - instance.add(files, msg); + instance.setTag(new VersionNumber(3, 1, 0)); + try { + instance.add(files, msg); + assertFalse("Une exception aurait du etre leve, car instance est" + + " sur un tag, donc en readonly", true); + } catch(VCSException eee) { + assertTrue("On a bien pas la droit d'ajouter des fichiers", true); + } } /**