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:
042: package org.netbeans.modules.subversion.ui.update;
043:
044: import java.util.Iterator;
045: import org.netbeans.modules.subversion.ui.actions.ContextAction;
046: import org.netbeans.modules.subversion.util.Context;
047: import org.netbeans.modules.subversion.*;
048: import java.io.File;
049: import java.util.ArrayList;
050: import java.util.List;
051: import javax.swing.SwingUtilities;
052: import org.netbeans.modules.subversion.client.SvnClient;
053: import org.netbeans.modules.subversion.client.SvnClientExceptionHandler;
054: import org.netbeans.modules.subversion.client.SvnProgressSupport;
055: import org.netbeans.modules.subversion.util.SvnUtils;
056: import org.netbeans.modules.versioning.util.Utils;
057: import org.netbeans.modules.versioning.util.VersioningOutputManager;
058: import org.openide.nodes.Node;
059: import org.openide.awt.StatusDisplayer;
060: import org.openide.util.RequestProcessor;
061: import org.tigris.subversion.svnclientadapter.ISVNNotifyListener;
062: import org.tigris.subversion.svnclientadapter.ISVNStatus;
063: import org.tigris.subversion.svnclientadapter.SVNClientException;
064: import org.tigris.subversion.svnclientadapter.SVNNodeKind;
065: import org.tigris.subversion.svnclientadapter.SVNRevision;
066: import org.tigris.subversion.svnclientadapter.SVNUrl;
067: import org.tigris.subversion.svnclientadapter.utils.SVNStatusUtils;
068:
069: /**
070: * Update action
071: *
072: * @author Petr Kuzel
073: */
074: public class UpdateAction extends ContextAction {
075:
076: protected String getBaseName(Node[] nodes) {
077: return "CTL_MenuItem_Update"; // NOI18N
078: }
079:
080: protected int getFileEnabledStatus() {
081: return FileInformation.STATUS_IN_REPOSITORY;
082: }
083:
084: protected int getDirectoryEnabledStatus() {
085: return FileInformation.STATUS_MANAGED
086: & ~FileInformation.STATUS_NOTVERSIONED_EXCLUDED
087: & ~FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY;
088: }
089:
090: protected void performContextAction(Node[] nodes) {
091: if (!Subversion.getInstance().checkClientAvailable()) {
092: return;
093: }
094: performUpdate(nodes);
095: }
096:
097: private void performUpdate(final Node[] nodes) {
098: // FIXME add shalow logic allowing to ignore nested projects
099: // look into CVS, it's very tricky:
100: // project1/
101: // nbbuild/ (project1)
102: // project2/
103: // src/ (project1)
104: // test/ (project1 but imagine it's in repository, to be updated )
105: // Is there a way how to update project1 without updating project2?
106: final Context ctx = getContext(nodes);
107: ContextAction.ProgressSupport support = new ContextAction.ProgressSupport(
108: this , nodes) {
109: public void perform() {
110: update(ctx, this , getContextDisplayName(nodes));
111: }
112: };
113: support.start(createRequestProcessor(nodes));
114: }
115:
116: private static void update(Context ctx,
117: SvnProgressSupport progress, String contextDisplayName) {
118:
119: File[] roots = ctx.getRootFiles();
120:
121: final SVNUrl repositoryUrl;
122: try {
123: repositoryUrl = SvnUtils.getRepositoryRootUrl(roots[0]);
124: } catch (SVNClientException ex) {
125: SvnClientExceptionHandler.notifyException(ex, true, true);
126: return;
127: }
128:
129: FileStatusCache cache = Subversion.getInstance()
130: .getStatusCache();
131: cache.refreshCached(ctx);
132: File[][] split = Utils.splitFlatOthers(roots);
133: final List<File> recursiveFiles = new ArrayList<File>();
134: final List<File> flatFiles = new ArrayList<File>();
135:
136: // recursive files
137: for (int i = 0; i < split[1].length; i++) {
138: recursiveFiles.add(split[1][i]);
139: }
140: // flat files
141: //File[] flatRoots = SvnUtils.flatten(split[0], getDirectoryEnabledStatus());
142: for (int i = 0; i < split[0].length; i++) {
143: flatFiles.add(split[0][i]);
144: }
145:
146: SvnClient client;
147: UpdateOutputListener listener = new UpdateOutputListener();
148: try {
149: client = Subversion.getInstance().getClient(repositoryUrl);
150: client.addNotifyListener(listener);
151: } catch (SVNClientException ex) {
152: SvnClientExceptionHandler.notifyException(ex, true, true);
153: return;
154: }
155:
156: try {
157: updateRoots(recursiveFiles, progress, client, true);
158: if (progress.isCanceled()) {
159: return;
160: }
161: updateRoots(flatFiles, progress, client, false);
162: } catch (SVNClientException e1) {
163: progress.annotate(e1);
164: } finally {
165: openResults(listener.getResults(), repositoryUrl,
166: contextDisplayName);
167: }
168: }
169:
170: private static void openResults(
171: final List<FileUpdateInfo> resultsList, final SVNUrl url,
172: final String contextDisplayName) {
173: SwingUtilities.invokeLater(new Runnable() {
174: public void run() {
175: UpdateResults results = new UpdateResults(resultsList,
176: url, contextDisplayName);
177: VersioningOutputManager vom = VersioningOutputManager
178: .getInstance();
179: vom.addComponent(url.toString() + "-UpdateExecutor",
180: results); // NOI18N
181: }
182: });
183: }
184:
185: private static void updateRoots(List<File> roots,
186: SvnProgressSupport support, SvnClient client,
187: boolean recursive) throws SVNClientException {
188: boolean conflict = false;
189: for (Iterator<File> it = roots.iterator(); it.hasNext();) {
190: File root = it.next();
191: if (support.isCanceled()) {
192: break;
193: }
194: client.update(root, SVNRevision.HEAD, recursive);
195:
196: // XXX this isn't clean - the cache gets notified only about files which realy were updated.
197: // However, the revision in the metadata is set to HEAD even if the file didn't change =>
198: // the status is refresh twice for each file which was updated just because we want to refresh
199: // the cahced revision value...
200: if (recursive) {
201: SvnUtils.refreshRecursively(root);
202: } else {
203: FileStatusCache cache = Subversion.getInstance()
204: .getStatusCache();
205: cache.onNotify(root, null);
206: if (root.isDirectory()) {
207: File[] files = root.listFiles();
208: if (files != null) {
209: for (File f : files) {
210: cache.onNotify(f, null);
211: }
212: }
213: }
214: }
215: ISVNStatus status[] = client.getStatus(root, true, false);
216: for (int k = 0; k < status.length; k++) {
217: ISVNStatus s = status[k];
218: if (SVNStatusUtils.isTextConflicted(s)
219: || SVNStatusUtils.isPropConflicted(s)) {
220: conflict = true;
221: }
222: }
223: }
224: if (conflict) {
225: StatusDisplayer.getDefault()
226: .setStatusText(
227: org.openide.util.NbBundle.getMessage(
228: UpdateAction.class,
229: "MSG_Update_Conflicts")); // NOI18N
230: } else {
231: StatusDisplayer.getDefault()
232: .setStatusText(
233: org.openide.util.NbBundle.getMessage(
234: UpdateAction.class,
235: "MSG_Update_Completed")); // NOI18N
236: }
237: return;
238: }
239:
240: public static void performUpdate(final Context context,
241: final String contextDisplayName) {
242: if (!Subversion.getInstance().checkClientAvailable()) {
243: return;
244: }
245: if (context == null || context.getRoots().size() == 0) {
246: return;
247: }
248:
249: SVNUrl repository;
250: try {
251: repository = getSvnUrl(context);
252: } catch (SVNClientException ex) {
253: SvnClientExceptionHandler.notifyException(ex, true, true);
254: return;
255: }
256:
257: RequestProcessor rp = Subversion.getInstance()
258: .getRequestProcessor(repository);
259: SvnProgressSupport support = new SvnProgressSupport() {
260: public void perform() {
261: update(context, this , contextDisplayName);
262: }
263: };
264: support.start(rp, repository, org.openide.util.NbBundle
265: .getMessage(UpdateAction.class, "MSG_Update_Progress")); // NOI18N
266: }
267:
268: private static class UpdateOutputListener implements
269: ISVNNotifyListener {
270:
271: private List<FileUpdateInfo> results;
272:
273: public void setCommand(int command) {
274: }
275:
276: public void logCommandLine(String str) {
277: }
278:
279: public void logMessage(String logMsg) {
280: FileUpdateInfo[] fuis = FileUpdateInfo
281: .createFromLogMsg(logMsg);
282: if (fuis != null) {
283: for (FileUpdateInfo fui : fuis) {
284: if (fui != null)
285: getResults().add(fui);
286: }
287: }
288: }
289:
290: public void logError(String str) {
291: }
292:
293: public void logRevision(long rev, String str) {
294: }
295:
296: public void logCompleted(String str) {
297: }
298:
299: public void onNotify(File file, SVNNodeKind kind) {
300: }
301:
302: List<FileUpdateInfo> getResults() {
303: if (results == null) {
304: results = new ArrayList<FileUpdateInfo>();
305: }
306: return results;
307: }
308:
309: };
310:
311: }
|