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.ignore;
043:
044: import java.util.*;
045: import org.netbeans.modules.subversion.*;
046: import org.netbeans.modules.subversion.client.SvnClient;
047: import org.netbeans.modules.subversion.ui.actions.*;
048: import org.netbeans.modules.subversion.util.*;
049: import org.openide.*;
050: import org.openide.nodes.Node;
051: import java.io.File;
052: import java.lang.String;
053: import org.netbeans.modules.subversion.client.SvnClientExceptionHandler;
054: import org.tigris.subversion.svnclientadapter.*;
055:
056: /**
057: * Adds/removes files to svn:ignore property.
058: * It does not support patterns.
059: *
060: * @author Maros Sandor
061: */
062: public class IgnoreAction extends ContextAction {
063:
064: public static final int UNDEFINED = 0;
065: public static final int IGNORING = 1;
066: public static final int UNIGNORING = 2;
067:
068: protected String getBaseName(Node[] activatedNodes) {
069: int actionStatus = getActionStatus(activatedNodes);
070: switch (actionStatus) {
071: case UNDEFINED:
072: case IGNORING:
073: return "CTL_MenuItem_Ignore"; // NOI18N
074: case UNIGNORING:
075: return "CTL_MenuItem_Unignore"; // NOI18N
076: default:
077: throw new RuntimeException("Invalid action status: "
078: + actionStatus); // NOI18N
079: }
080: }
081:
082: protected int getFileEnabledStatus() {
083: return FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY
084: | FileInformation.STATUS_NOTVERSIONED_EXCLUDED;
085: }
086:
087: protected int getDirectoryEnabledStatus() {
088: return FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY
089: | FileInformation.STATUS_NOTVERSIONED_EXCLUDED;
090: }
091:
092: public int getActionStatus(Node[] nodes) {
093: return getActionStatus(SvnUtils.getCurrentContext(nodes)
094: .getFiles());
095: }
096:
097: public int getActionStatus(File[] files) {
098: int actionStatus = -1;
099: if (files.length == 0)
100: return UNDEFINED;
101: FileStatusCache cache = Subversion.getInstance()
102: .getStatusCache();
103: for (int i = 0; i < files.length; i++) {
104: if (files[i].getName().equals(".svn")
105: || files[i].getName().equals("_svn")) { // NOI18N
106: actionStatus = UNDEFINED;
107: break;
108: }
109: FileInformation info = cache.getStatus(files[i]);
110: if (info.getStatus() == FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY) {
111: if (actionStatus == UNIGNORING) {
112: actionStatus = UNDEFINED;
113: break;
114: }
115: actionStatus = IGNORING;
116: } else if (info.getStatus() == FileInformation.STATUS_NOTVERSIONED_EXCLUDED) {
117: if (actionStatus == IGNORING) {
118: actionStatus = UNDEFINED;
119: break;
120: }
121: actionStatus = UNIGNORING;
122: } else {
123: actionStatus = UNDEFINED;
124: break;
125: }
126: }
127: return actionStatus == -1 ? UNDEFINED : actionStatus;
128: }
129:
130: protected boolean enable(Node[] nodes) {
131: return getActionStatus(nodes) != UNDEFINED;
132: }
133:
134: public void performContextAction(final Node[] nodes) {
135: if (!Subversion.getInstance().checkClientAvailable()) {
136: return;
137: }
138: final int actionStatus = getActionStatus(nodes);
139: if (actionStatus != IGNORING && actionStatus != UNIGNORING) {
140: throw new RuntimeException("Invalid action status: "
141: + actionStatus); // NOI18N
142: }
143:
144: final File files[] = SvnUtils.getCurrentContext(nodes)
145: .getRootFiles();
146:
147: ContextAction.ProgressSupport support = new ContextAction.ProgressSupport(
148: this , nodes) {
149: public void perform() {
150: Map<File, Set<String>> names = splitByParent(files);
151: // do not attach onNotify listeners because the ignore command forcefully fires change events on ALL files
152: // in the parent directory and NONE of them interests us, see #89516
153: SvnClient client;
154: try {
155: client = Subversion.getInstance().getClient(false);
156: } catch (SVNClientException e) {
157: SvnClientExceptionHandler.notifyException(e, true,
158: true);
159: return;
160: }
161: for (File parent : names.keySet()) {
162: Set<String> patterns = names.get(parent);
163: if (isCanceled()) {
164: return;
165: }
166: try {
167: Set<String> currentPatterns = new HashSet<String>(
168: client.getIgnoredPatterns(parent));
169: if (actionStatus == IGNORING) {
170: ensureVersioned(parent);
171: currentPatterns.addAll(patterns);
172: } else if (actionStatus == UNIGNORING) {
173: currentPatterns.removeAll(patterns);
174: }
175: client.setIgnoredPatterns(parent,
176: new ArrayList<String>(currentPatterns));
177:
178: } catch (SVNClientException e) {
179: SvnClientExceptionHandler.notifyException(e,
180: true, true);
181: }
182: }
183: // refresh files manually, we do not suppport wildcards in ignore patterns so this is sufficient
184: for (File file : files) {
185: Subversion.getInstance().getStatusCache().refresh(
186: file,
187: FileStatusCache.REPOSITORY_STATUS_UNKNOWN);
188: }
189: // refresh also the parents
190: for (File parent : names.keySet()) {
191: Subversion.getInstance().getStatusCache().refresh(
192: parent,
193: FileStatusCache.REPOSITORY_STATUS_UNKNOWN);
194: }
195: }
196: };
197: support.start(createRequestProcessor(nodes));
198: }
199:
200: private Map<File, Set<String>> splitByParent(File[] files) {
201: Map<File, Set<String>> map = new HashMap<File, Set<String>>(2);
202: for (File file : files) {
203: File parent = file.getParentFile();
204: if (parent == null)
205: continue;
206: Set<String> names = map.get(parent);
207: if (names == null) {
208: names = new HashSet<String>(5);
209: map.put(parent, names);
210: }
211: names.add(file.getName());
212: }
213: return map;
214: }
215:
216: /**
217: * Adds this file and all its parent folders to repository if they are not yet added.
218: *
219: * @param file file to add
220: * @throws SVNClientException if something goes wrong in subversion
221: */
222: private static void ensureVersioned(File file)
223: throws SVNClientException {
224: FileStatusCache cache = Subversion.getInstance()
225: .getStatusCache();
226: if ((cache.getStatus(file).getStatus() & FileInformation.STATUS_VERSIONED) != 0)
227: return;
228: ensureVersioned(file.getParentFile());
229: add(file);
230: cache.refresh(file, FileStatusCache.REPOSITORY_STATUS_UNKNOWN);
231: }
232:
233: /**
234: * Adds the file to repository with 'svn add', non-recursively.
235: *
236: * @param file file to add
237: */
238: private static void add(File file) throws SVNClientException {
239: SVNUrl repositoryUrl = SvnUtils.getRepositoryRootUrl(file);
240: SvnClient client = Subversion.getInstance().getClient(
241: repositoryUrl);
242: client.addFile(file);
243: }
244:
245: protected boolean asynchronous() {
246: return false;
247: }
248:
249: public static void ignore(File file) throws SVNClientException {
250: File parent = file.getParentFile();
251: ensureVersioned(parent);
252: // technically, this block need not be synchronized but we want to have svn:ignore property set correctly at all times
253: synchronized (IgnoreAction.class) {
254: List<String> patterns = Subversion.getInstance().getClient(
255: true).getIgnoredPatterns(parent);
256: if (patterns.contains(file.getName()) == false) {
257: patterns.add(file.getName());
258: Subversion.getInstance().getClient(true)
259: .setIgnoredPatterns(parent, patterns);
260: }
261: }
262: }
263: }
|