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.versioning.system.cvss.ui.selectors;
043:
044: import org.netbeans.lib.cvsclient.CVSRoot;
045: import org.netbeans.lib.cvsclient.Client;
046: import org.netbeans.lib.cvsclient.connection.*;
047: import org.netbeans.lib.cvsclient.event.*;
048: import org.netbeans.lib.cvsclient.command.checkout.CheckoutCommand;
049: import org.netbeans.lib.cvsclient.command.GlobalOptions;
050: import org.netbeans.lib.cvsclient.command.CommandException;
051: import org.netbeans.lib.cvsclient.command.log.LogCommand;
052: import org.netbeans.lib.cvsclient.command.log.LogInformation;
053: import org.netbeans.modules.versioning.system.cvss.CvsVersioningSystem;
054: import org.netbeans.modules.versioning.system.cvss.FileInformation;
055: import org.netbeans.modules.versioning.system.cvss.util.Utils;
056: import org.openide.util.RequestProcessor;
057: import org.openide.util.UserCancelException;
058: import org.openide.util.HelpCtx;
059: import org.openide.ErrorManager;
060: import org.openide.nodes.Node;
061: import org.openide.nodes.AbstractNode;
062: import org.openide.nodes.NodeAcceptor;
063:
064: import java.util.*;
065: import java.util.List;
066: import java.io.File;
067: import java.io.IOException;
068:
069: /**
070: * Allows to select branches for given repository path,
071: *
072: * @author Petr Kuzel
073: */
074: public final class BranchSelector implements Runnable {
075:
076: private CVSRoot root;
077:
078: private String module;
079:
080: private File file;
081:
082: private Node rootNode;
083: private BranchNodeChildren rootKids;
084:
085: /**
086: s * Selects tag or branch for versioned files. Shows modal UI.
087: *
088: * @param file versioned file or folder
089: * @return selected tag or <code>null</code> on cancel.
090: */
091: public String selectTag(File file) {
092:
093: this .file = file;
094:
095: return showSelector();
096: }
097:
098: /**
099: * Selects tag or branch for not yet locally checked out files.
100: *
101: * @param root repository
102: * @param module hint where to look for the first cvs_loggable file
103: * <ul>
104: * <li><code>null</code> is not allowed
105: * <li><code>"."</code> stays for any modules
106: * </ul>
107: * @return selected tag or <code>null</code> on cancel.
108: */
109: public String selectTag(CVSRoot root, String module) {
110:
111: this .root = root;
112: this .module = module;
113:
114: return showSelector();
115: }
116:
117: private String showSelector() {
118:
119: rootKids = new BranchNodeChildren();
120: rootNode = new AbstractNode(rootKids);
121:
122: // load on background
123: RequestProcessor.getDefault().post(this );
124:
125: try {
126: NodeOperation2 op = new NodeOperation2();
127: op.setIconsVisible(false);
128: op.setRootVisible(false);
129: op.setHelpCtx(new HelpCtx(BranchSelector.class));
130: Node[] selected = op.select(org.openide.util.NbBundle
131: .getMessage(BranchSelector.class, "BK2012"),
132: org.openide.util.NbBundle.getMessage(
133: BranchSelector.class, "BK2013"),
134: org.openide.util.NbBundle.getMessage(
135: BranchSelector.class, "ACSD_BranchSelect"),
136: rootNode, org.openide.util.NbBundle.getMessage(
137: BranchSelector.class, "ACSN_BranchesTree"),
138: org.openide.util.NbBundle.getMessage(
139: BranchSelector.class, "ACSD_BranchesTree"),
140: new NodeAcceptor() {
141: public boolean acceptNodes(Node[] nodes) {
142: if (nodes.length != 1)
143: return false;
144: return nodes[0].getLookup().lookup(
145: String.class) != null;
146: }
147: });
148:
149: Node node = selected[0];
150: String branch = (String) node.getLookup().lookup(
151: String.class);
152: return branch;
153: } catch (UserCancelException e) {
154: return null;
155: }
156: }
157:
158: /**
159: * 1. Checkout (non-recursively) content of the module
160: * 2. Return list of all normal files checked out
161: *
162: * In case the checkout does not create any normal files (only folders) try to dive into those until we get some
163: * real files that we can log.
164: *
165: * @param moduleName
166: * @return never returns null
167: */
168: private File[] getLoggableFiles(String moduleName)
169: throws CommandException, AuthenticationException {
170:
171: GlobalOptions gtx = CvsVersioningSystem.createGlobalOptions();
172: if (root != null) {
173: gtx.setCVSRoot(root.toString()); // XXX why is it needed? Client already knows, who is definitive source of cvs root?
174: }
175:
176: File checkoutFolder = Kit.createTmpFolder();
177: if (checkoutFolder == null) {
178: error(org.openide.util.NbBundle.getMessage(
179: BranchSelector.class, "BK2015"));
180: return new File[0];
181: }
182:
183: CheckoutCommand checkout = new CheckoutCommand();
184: checkout.setRecursive(false);
185:
186: // non recursive operation doe not work with "." module
187: // #58208 so here a random one is choosen
188: if (".".equals(moduleName)) { // NOI18N
189: Client client = Kit.createClient(root);
190: List l = ModuleSelector
191: .listRepositoryPath(client, root, ""); // NOI18N
192: int max = l.size();
193: int counter = max;
194: Random random = new Random();
195: while (counter-- > 0) {
196: int rnd = random.nextInt(max);
197: String path = (String) l.get(rnd);
198: if ("CVSROOT".equals(path))
199: continue; // NOI18N
200: moduleName = path;
201: break;
202: }
203: }
204: checkout.setModule(moduleName);
205: File[] checkoutFiles = new File[] { checkoutFolder };
206: checkout.setFiles(checkoutFiles);
207:
208: Client client = Kit.createClient(root);
209: client.setLocalPath(checkoutFolder.getAbsolutePath());
210: client.executeCommand(checkout, gtx);
211:
212: File folderToCheck = new File(checkoutFolder, moduleName);
213: if (!folderToCheck.isDirectory()) {
214: folderToCheck = checkoutFolder;
215: }
216:
217: List<File> filesToLog = new ArrayList<File>();
218: for (File child : folderToCheck.listFiles()) {
219: if ("CVSROOT".equals(child.getName()))
220: continue; // NOI18N
221: if ("CVS".equals(child.getName()))
222: continue; // NOI18N
223: filesToLog.add(child);
224:
225: }
226: if (filesToLog.size() > 0)
227: return (File[]) filesToLog.toArray(new File[filesToLog
228: .size()]);
229:
230: // there are no files to log, let us dive deeper
231: client = Kit.createClient(root);
232: List<String> fileList = ModuleSelector.listRepositoryPath(
233: client, root, moduleName);
234:
235: for (String child : fileList) {
236: String childModule = moduleName + "/" + child;
237: File[] childLoggable = getLoggableFiles(childModule);
238: if (childLoggable.length > 0)
239: return childLoggable;
240: }
241:
242: // tried hard but there are no files to log for the module
243: return new File[0];
244: }
245:
246: /** Background runnable*/
247: public void run() {
248:
249: GlobalOptions gtx = CvsVersioningSystem.createGlobalOptions();
250: if (root != null) {
251: gtx.setCVSRoot(root.toString()); // XXX why is it needed? Client already knows, who is definitive source of cvs root?
252: }
253: File checkoutFolder = null;
254: try {
255:
256: File[] files;
257: File localPath;
258: if (file == null) {
259:
260: files = getLoggableFiles(module);
261: if (files.length > 0) {
262: localPath = files[0].getParentFile();
263: } else {
264: localPath = null;
265: }
266:
267: } else {
268: if (file.isDirectory()) {
269: files = file.listFiles();
270: localPath = file;
271: } else {
272: files = new File[] { file };
273: localPath = file.getParentFile();
274: }
275: }
276:
277: List logFiles = new ArrayList(files.length);
278: fillLogFiles(files, logFiles);
279: if (logFiles.isEmpty()) {
280: tagsLoaded(Collections.EMPTY_SET, Collections.EMPTY_SET);
281: return;
282: }
283:
284: // extract tags using log
285: LogCommand log = new LogCommand();
286: log.setHeaderOnly(true);
287: File[] cmdFiles = (File[]) logFiles
288: .toArray(new File[logFiles.size()]);
289: log.setFiles(cmdFiles);
290: if (root == null) {
291: for (int i = 0; i < cmdFiles.length; i++) {
292: try {
293: root = CVSRoot.parse(Utils
294: .getCVSRootFor(cmdFiles[i])); // raises exception
295: break;
296: } catch (IOException e) {
297: ErrorManager err = ErrorManager.getDefault();
298: err.annotate(e, "Can not find CVSROOT for "
299: + cmdFiles[i]); // NOI18N
300: err.notify(ErrorManager.INFORMATIONAL, e);
301: }
302: }
303: }
304: Client client = Kit.createClient(root);
305:
306: final Set tags = new TreeSet();
307: final Set branches = new TreeSet();
308: EventManager mgr = client.getEventManager();
309: mgr.addCVSListener(new CVSListener() {
310:
311: public void messageSent(MessageEvent e) {
312: }
313:
314: public void messageSent(BinaryMessageEvent e) {
315: }
316:
317: public void fileAdded(FileAddedEvent e) {
318: }
319:
320: public void fileToRemove(FileToRemoveEvent e) {
321: }
322:
323: public void fileRemoved(FileRemovedEvent e) {
324: }
325:
326: public void fileUpdated(FileUpdatedEvent e) {
327: }
328:
329: public void fileInfoGenerated(FileInfoEvent e) {
330: LogInformation info = (LogInformation) e
331: .getInfoContainer();
332: List symNames = info.getAllSymbolicNames();
333: Iterator it = symNames.iterator();
334: while (it.hasNext()) {
335: LogInformation.SymName name = (LogInformation.SymName) it
336: .next();
337: if (name.isBranch()) {
338: branches.add(name.getName());
339: } else {
340: tags.add(name.getName());
341: }
342: }
343: }
344:
345: public void commandTerminated(TerminationEvent e) {
346: }
347:
348: public void moduleExpanded(ModuleExpansionEvent e) {
349: }
350: });
351:
352: client.setLocalPath(localPath.getAbsolutePath());
353: client.executeCommand(log, gtx);
354: tagsLoaded(branches, tags);
355: Kit.deleteRecursively(checkoutFolder);
356: } catch (CommandException e) {
357: error(org.openide.util.NbBundle.getMessage(
358: BranchSelector.class, "BK2016"));
359: ErrorManager err = ErrorManager.getDefault();
360: err.annotate(e, org.openide.util.NbBundle.getMessage(
361: BranchSelector.class, "BK2016"));
362: err.notify(e);
363: } catch (AuthenticationException e) {
364: error(org.openide.util.NbBundle.getMessage(
365: BranchSelector.class, "BK2016"));
366: ErrorManager err = ErrorManager.getDefault();
367: err.annotate(e, org.openide.util.NbBundle.getMessage(
368: BranchSelector.class, "BK2016"));
369: err.notify(e);
370: } finally {
371: Kit.deleteRecursively(checkoutFolder);
372: }
373: }
374:
375: /**
376: * Try to recursively locate at least one versioned file.
377: */
378: private void fillLogFiles(File[] files, List logFiles) {
379:
380: if (files == null) {
381: return;
382: }
383:
384: if (logFiles.isEmpty() == false) {
385: return;
386: }
387:
388: for (int i = 0; i < files.length; i++) {
389: if (files[i].isFile()) {
390: FileInformation info = CvsVersioningSystem
391: .getInstance().getStatusCache().getStatus(
392: files[i]);
393: if ((info.getStatus() & FileInformation.STATUS_IN_REPOSITORY) != 0) {
394: logFiles.add(files[i]);
395: }
396: }
397: }
398:
399: for (int i = 0; i < files.length; i++) {
400: if (files[i].isDirectory()) {
401: fillLogFiles(files[i].listFiles(), logFiles); // RESURSION
402: }
403: }
404:
405: }
406:
407: /**
408: * @param tags contains ModuleListInformation
409: */
410: private void tagsLoaded(Collection branches, Collection tags) {
411: rootKids.setBranches(branches);
412: rootKids.setTags(tags);
413: }
414:
415: private void error(String msg) {
416: rootNode.setDisplayName(msg);
417: }
418:
419: }
|