001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.mercurial.ui.clone;
042:
043: import java.io.IOException;
044: import org.netbeans.modules.versioning.spi.VCSContext;
045: import javax.swing.*;
046: import java.awt.event.ActionEvent;
047: import java.io.BufferedReader;
048: import java.io.File;
049: import java.io.FileReader;
050: import java.io.FileWriter;
051: import java.io.PrintWriter;
052: import java.util.List;
053: import org.netbeans.api.project.Project;
054: import org.netbeans.api.project.ProjectManager;
055: import org.netbeans.modules.mercurial.HgException;
056: import org.netbeans.modules.mercurial.HgProgressSupport;
057: import org.netbeans.modules.mercurial.Mercurial;
058: import org.netbeans.modules.mercurial.OutputLogger;
059: import org.netbeans.modules.mercurial.HgModuleConfig;
060: import org.netbeans.modules.mercurial.config.HgConfigFiles;
061: import org.netbeans.modules.mercurial.util.HgCommand;
062: import org.netbeans.modules.mercurial.util.HgUtils;
063: import org.netbeans.modules.mercurial.util.HgProjectUtils;
064: import org.netbeans.modules.mercurial.ui.actions.ContextAction;
065: import org.netbeans.modules.mercurial.ui.properties.HgProperties;
066: import org.openide.filesystems.FileObject;
067: import org.openide.filesystems.FileUtil;
068: import org.openide.DialogDisplayer;
069: import org.openide.NotifyDescriptor;
070: import org.openide.util.Cancellable;
071: import org.openide.util.NbBundle;
072: import org.openide.util.RequestProcessor;
073: import org.openide.util.Utilities;
074:
075: /**
076: * Clone action for mercurial:
077: * hg clone - Create a copy of an existing repository in a new directory.
078: *
079: * @author John Rice
080: */
081: public class CloneAction extends ContextAction {
082: private final VCSContext context;
083:
084: public CloneAction(String name, VCSContext context) {
085: this .context = context;
086: putValue(Action.NAME, name);
087: }
088:
089: public void performAction(ActionEvent ev) {
090: final File root = HgUtils.getRootFile(context);
091: if (root == null)
092: return;
093:
094: // Get unused Clone Folder name
095: File tmp = root.getParentFile();
096: File projFile = HgUtils.getProjectFile(context);
097: String folderName = root.getName();
098: Boolean projIsRepos = true;
099: if (!root.equals(projFile)) {
100: // Mercurial Repository is not the same as project root
101: projIsRepos = false;
102: }
103: for (int i = 0; i < 10000; i++) {
104: if (!new File(tmp, folderName + "_clone" + i).exists()) { // NOI18N
105: tmp = new File(tmp, folderName + "_clone" + i); // NOI18N
106: break;
107: }
108: }
109: Clone clone = new Clone(root, tmp);
110: if (!clone.showDialog()) {
111: return;
112: }
113: performClone(root.getAbsolutePath(), clone.getOutputFileName(),
114: projIsRepos, projFile, true, null, null);
115: }
116:
117: public static void performClone(final String source,
118: final String target, boolean projIsRepos, File projFile,
119: final String pullPath, final String pushPath) {
120: performClone(source, target, projIsRepos, projFile, false,
121: pullPath, pushPath);
122: }
123:
124: private static void performClone(final String source,
125: final String target, boolean projIsRepos, File projFile,
126: final boolean isLocalClone, final String pullPath,
127: final String pushPath) {
128: final Mercurial hg = Mercurial.getInstance();
129: final ProjectManager projectManager = ProjectManager
130: .getDefault();
131: final File prjFile = projFile;
132: final Boolean prjIsRepos = projIsRepos;
133: final File cloneFolder = new File(target);
134: final File normalizedCloneFolder = FileUtil
135: .normalizeFile(cloneFolder);
136: String projName = null;
137: if (projFile != null)
138: projName = HgProjectUtils.getProjectName(projFile);
139: final String prjName = projName;
140: File cloneProjFile;
141: if (!prjIsRepos) {
142: String name = null;
143: if (prjFile != null)
144: name = prjFile.getAbsolutePath().substring(
145: source.length() + 1);
146: else
147: name = target;
148: cloneProjFile = new File(normalizedCloneFolder, name);
149: } else {
150: cloneProjFile = normalizedCloneFolder;
151: }
152: final File clonePrjFile = cloneProjFile;
153:
154: RequestProcessor rp = Mercurial.getInstance()
155: .getRequestProcessor(source);
156: final HgProgressSupport support = new HgProgressSupport() {
157: Runnable doOpenProject = new Runnable() {
158: public void run() {
159: // Open and set focus on the cloned project if possible
160: OutputLogger logger = getLogger();
161: try {
162: FileObject cloneProj = FileUtil
163: .toFileObject(clonePrjFile);
164: Project prj = null;
165: if (clonePrjFile != null && cloneProj != null)
166: prj = projectManager.findProject(cloneProj);
167: if (prj != null) {
168: HgProjectUtils.openProject(prj, this ,
169: HgModuleConfig.getDefault()
170: .getSetMainProject());
171: hg.versionedFilesChanged();
172: hg.refreshAllAnnotations();
173: } else {
174: logger
175: .outputInRed(NbBundle
176: .getMessage(
177: CloneAction.class,
178: "MSG_EXTERNAL_CLONE_PRJ_NOT_FOUND_CANT_SETASMAIN")); // NOI18N
179: }
180:
181: } catch (java.lang.Exception ex) {
182: NotifyDescriptor.Exception e = new NotifyDescriptor.Exception(
183: new HgException(ex.toString()));
184: DialogDisplayer.getDefault().notifyLater(e);
185: } finally {
186: logger.outputInRed(NbBundle.getMessage(
187: CloneAction.class, "MSG_CLONE_DONE")); // NOI18N
188: logger.output(""); // NOI18N
189: }
190: }
191: };
192:
193: public void perform() {
194: OutputLogger logger = getLogger();
195: try {
196: // TODO: We need to annotate the cloned project
197: // See http://qa.netbeans.org/issues/show_bug.cgi?id=112870
198: logger.outputInRed(NbBundle.getMessage(
199: CloneAction.class, "MSG_CLONE_TITLE")); // NOI18N
200: logger.outputInRed(NbBundle.getMessage(
201: CloneAction.class, "MSG_CLONE_TITLE_SEP")); // NOI18N
202: List<String> list = HgCommand.doClone(source,
203: target, logger);
204: if (list != null && !list.isEmpty()) {
205: HgUtils.createIgnored(cloneFolder);
206: logger.output(list);
207:
208: if (prjName != null) {
209: logger.outputInRed(NbBundle.getMessage(
210: CloneAction.class,
211: "MSG_CLONE_FROM", prjName, source)); // NOI18N
212: logger.outputInRed(NbBundle.getMessage(
213: CloneAction.class, "MSG_CLONE_TO",
214: prjName, target)); // NOI18N
215: } else {
216: logger.outputInRed(NbBundle.getMessage(
217: CloneAction.class,
218: "MSG_EXTERNAL_CLONE_FROM", source)); // NOI18N
219: logger.outputInRed(NbBundle.getMessage(
220: CloneAction.class,
221: "MSG_EXTERNAL_CLONE_TO", target)); // NOI18N
222:
223: }
224: logger.output(""); // NOI18N
225:
226: if (isLocalClone) {
227: SwingUtilities.invokeLater(doOpenProject);
228: } else if (HgModuleConfig.getDefault()
229: .getShowCloneCompleted()) {
230: CloneCompleted cc = new CloneCompleted(
231: cloneFolder);
232: if (isCanceled()) {
233: return;
234: }
235: cc.scanForProjects(this );
236: }
237: }
238: } catch (HgException ex) {
239: NotifyDescriptor.Exception e = new NotifyDescriptor.Exception(
240: ex);
241: DialogDisplayer.getDefault().notifyLater(e);
242: } finally {
243: //#121581: Work around for ini4j bug on Windows not handling single '\' correctly
244: // hg clone creates the default hgrc, we just overwrite it's contents with
245: // default path contianing '\\'
246: if (isLocalClone && Utilities.isWindows()) {
247: fixLocalPullPushPathsOnWindows(cloneFolder
248: .getAbsolutePath());
249: }
250: // #125835 - Push to default was not being set automatically by hg after Clone
251: // but was after you opened the Mercurial -> Properties, inconsistent
252: HgConfigFiles hg = new HgConfigFiles(cloneFolder);
253: String defaultPull = hg.getDefaultPull(false);
254: String defaultPush = hg.getDefaultPush(false);
255: if (pullPath != null && !pullPath.equals(""))
256: defaultPull = pullPath;
257: if (pushPath != null && !pushPath.equals(""))
258: defaultPush = pushPath;
259: hg.setProperty(
260: HgProperties.HGPROPNAME_DEFAULT_PULL,
261: defaultPull);
262: hg.setProperty(
263: HgProperties.HGPROPNAME_DEFAULT_PUSH,
264: defaultPush);
265:
266: if (!isLocalClone) {
267: logger.outputInRed(NbBundle.getMessage(
268: CloneAction.class, "MSG_CLONE_DONE")); // NOI18N
269: logger.output(""); // NOI18N
270: }
271: }
272: }
273: };
274: support.setRepositoryRoot(source);
275: support.setCancellableDelegate(new Cancellable() {
276: public boolean cancel() {
277: if (!Utilities.isWindows())
278: return true;
279:
280: OutputLogger logger = support.getLogger();
281: logger.outputInRed(NbBundle.getMessage(
282: CloneAction.class, "MSG_CLONE_CANCEL_ATTEMPT")); // NOI18N
283: JOptionPane
284: .showMessageDialog(
285: null,
286: NbBundle
287: .getMessage(CloneAction.class,
288: "MSG_CLONE_CANCEL_NOT_SUPPORTED"),// NOI18N
289: NbBundle
290: .getMessage(CloneAction.class,
291: "MSG_CLONE_CANCEL_NOT_SUPPORTED_TITLE"),// NOI18N
292: JOptionPane.INFORMATION_MESSAGE);
293: return false;
294: }
295: });
296: support.start(rp, source, org.openide.util.NbBundle.getMessage(
297: CloneAction.class, "LBL_Clone_Progress", source)); // NOI18N
298: }
299:
300: public boolean isEnabled() {
301: return HgUtils.getRootFile(context) != null;
302: }
303:
304: private static final String HG_PATHS_SECTION_ENCLOSED = "["
305: + HgConfigFiles.HG_PATHS_SECTION + "]";// NOI18N
306:
307: private static void fixLocalPullPushPathsOnWindows(String root) {
308: File hgrcFile = null;
309: File tempFile = null;
310: BufferedReader br = null;
311: PrintWriter pw = null;
312:
313: try {
314: hgrcFile = new File(root + File.separator
315: + HgConfigFiles.HG_REPO_DIR,
316: HgConfigFiles.HG_RC_FILE);
317: if (!hgrcFile.isFile() || !hgrcFile.canWrite())
318: return;
319:
320: tempFile = new File(hgrcFile.getAbsolutePath() + ".tmp"); // NOI18N
321: if (tempFile == null)
322: return;
323:
324: br = new BufferedReader(new FileReader(hgrcFile));
325: pw = new PrintWriter(new FileWriter(tempFile));
326:
327: String line = null;
328:
329: boolean bInPaths = false;
330: boolean bPullDone = false;
331: boolean bPushDone = false;
332: while ((line = br.readLine()) != null) {
333: if (line.startsWith(HG_PATHS_SECTION_ENCLOSED)) {
334: bInPaths = true;
335: } else if (line.startsWith("[")) { // NOI18N
336: bInPaths = false;
337: }
338:
339: if (bInPaths
340: && !bPullDone
341: && line
342: .startsWith(HgConfigFiles.HG_DEFAULT_PULL_VALUE)
343: && !line
344: .startsWith(HgConfigFiles.HG_DEFAULT_PUSH_VALUE)) {
345: pw.println(line.replace("\\", "\\\\"));
346: bPullDone = true;
347: } else if (bInPaths
348: && !bPullDone
349: && line
350: .startsWith(HgConfigFiles.HG_DEFAULT_PULL)) {
351: pw.println(line.replace("\\", "\\\\"));
352: bPullDone = true;
353: } else if (bInPaths
354: && !bPushDone
355: && line
356: .startsWith(HgConfigFiles.HG_DEFAULT_PUSH_VALUE)) {
357: pw.println(line.replace("\\", "\\\\"));
358: bPushDone = true;
359: } else {
360: pw.println(line);
361: pw.flush();
362: }
363: }
364: } catch (IOException ex) {
365: // Ignore
366: } finally {
367: try {
368: if (pw != null)
369: pw.close();
370: if (br != null)
371: br.close();
372: if (tempFile != null && tempFile.isFile()
373: && tempFile.canWrite() && hgrcFile != null) {
374: hgrcFile.delete();
375: tempFile.renameTo(hgrcFile);
376: }
377: } catch (IOException ex) {
378: // Ignore
379: }
380: }
381: }
382: }
|