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.lib.cvsclient.command.export;
042:
043: import java.io.*;
044: import java.util.*;
045:
046: import org.netbeans.lib.cvsclient.*;
047: import org.netbeans.lib.cvsclient.command.*;
048: import org.netbeans.lib.cvsclient.connection.*;
049: import org.netbeans.lib.cvsclient.event.*;
050: import org.netbeans.lib.cvsclient.request.*;
051:
052: /**
053: * The export command exports the projects (modules in the repository)
054: * to the local directory structure.
055: *
056: * @author MIlos Kleint
057: */
058: public class ExportCommand extends RepositoryCommand {
059:
060: private final String UPDATING = ": Updating "; // NOI18N
061:
062: /**
063: * A store of potentially empty directories. When a directory has a file
064: * in it, it is removed from this set. This set allows the prune option
065: * to be implemented.
066: */
067: private final Set emptyDirectories = new HashSet();
068: private boolean pruneDirectories;
069: private KeywordSubstitutionOptions keywordSubstitutionOptions;
070:
071: /** Holds value of property exportByDate. */
072: private String exportByDate;
073:
074: /** Holds value of property exportByRevision. */
075: private String exportByRevision;
076:
077: /** Holds value of property exportDirectory. */
078: private String exportDirectory;
079:
080: /** Holds value of property useHeadIfNotFound. */
081: private boolean useHeadIfNotFound;
082:
083: /** Don't shorten module paths if -d specified. */
084: private boolean notShortenPaths;
085:
086: /** Do not run module program (if any). */
087: private boolean notRunModuleProgram;
088:
089: public ExportCommand() {
090: resetCVSCommand();
091: }
092:
093: /**
094: * Returns the keyword substitution option.
095: */
096: public KeywordSubstitutionOptions getKeywordSubstitutionOptions() {
097: return keywordSubstitutionOptions;
098: }
099:
100: /**
101: * Sets the keywords substitution option.
102: */
103: public void setKeywordSubstitutionOptions(
104: KeywordSubstitutionOptions keywordSubstitutionOptions) {
105: this .keywordSubstitutionOptions = keywordSubstitutionOptions;
106: }
107:
108: /**
109: * Set whether to prune directories.
110: * This is the -P option in the command-line CVS.
111: */
112: public void setPruneDirectories(boolean pruneDirectories) {
113: this .pruneDirectories = pruneDirectories;
114: }
115:
116: /**
117: * Get whether to prune directories.
118: * @return true if directories should be removed if they contain no files,
119: * false otherwise.
120: */
121: public boolean isPruneDirectories() {
122: return pruneDirectories;
123: }
124:
125: /**
126: * Execute this command
127: * @param client the client services object that provides any necessary
128: * services to this command, including the ability to actually process
129: * all the requests
130: */
131: protected void postExpansionExecute(ClientServices client,
132: EventManager em) throws CommandException,
133: AuthenticationException {
134: //
135: // moved modules code to the end of the other arguments --GAR
136: //
137: final int FIRST_INDEX = 0;
138: final int SECOND_INDEX = 1;
139: if (!isRecursive()) {
140: requests.add(FIRST_INDEX, new ArgumentRequest("-l")); //NOI18N
141: }
142: if (useHeadIfNotFound) {
143: requests.add(FIRST_INDEX, new ArgumentRequest("-f")); //NOI18N
144: }
145: if (exportDirectory != null && (!exportDirectory.equals(""))) {
146: requests.add(FIRST_INDEX, new ArgumentRequest("-d")); //NOI18N
147: requests.add(SECOND_INDEX, new ArgumentRequest(
148: getExportDirectory()));
149: }
150: if (exportByDate != null && exportByDate.length() > 0) {
151: requests.add(FIRST_INDEX, new ArgumentRequest("-D")); //NOI18N
152: requests.add(SECOND_INDEX, new ArgumentRequest(
153: getExportByDate()));
154: }
155: if (exportByRevision != null && exportByRevision.length() > 0) {
156: requests.add(FIRST_INDEX, new ArgumentRequest("-r")); //NOI18N
157: requests.add(SECOND_INDEX, new ArgumentRequest(
158: getExportByRevision()));
159: }
160: if (notShortenPaths) {
161: requests.add(FIRST_INDEX, new ArgumentRequest("-N")); //NOI18N
162: }
163: if (notRunModuleProgram) {
164: requests.add(FIRST_INDEX, new ArgumentRequest("-n")); //NOI18N
165: }
166: if (getKeywordSubstitutionOptions() != null) {
167: requests.add(new ArgumentRequest("-k"
168: + getKeywordSubstitutionOptions())); //NOI18N
169: }
170:
171: addArgumentRequests();
172:
173: requests.add(new DirectoryRequest(".", client.getRepository())); //NOI18N
174: requests.add(CommandRequest.EXPORT);
175: try {
176: client.processRequests(requests);
177: if (pruneDirectories) {
178: pruneEmptyDirectories();
179: }
180: requests.clear();
181:
182: } catch (CommandException ex) {
183: throw ex;
184: } catch (Exception ex) {
185: throw new CommandException(ex, ex.getLocalizedMessage());
186: } finally {
187: removeAllCVSAdminFiles();
188: }
189: }
190:
191: private void removeAllCVSAdminFiles() {
192: File rootDirect = null;
193: if (getExportDirectory() != null) {
194: rootDirect = new File(getLocalDirectory(),
195: getExportDirectory());
196: deleteCVSSubDirs(rootDirect);
197: } else {
198: rootDirect = new File(getLocalDirectory());
199: Iterator mods = expandedModules.iterator();
200: while (mods.hasNext()) {
201: String mod = mods.next().toString();
202: File modRoot = new File(rootDirect.getAbsolutePath(),
203: mod);
204: deleteCVSSubDirs(modRoot);
205: }
206: }
207: }
208:
209: private void deleteCVSSubDirs(File root) {
210: if (root.isDirectory()) {
211: File[] subDirs = root.listFiles();
212: if (subDirs == null) {
213: return;
214: }
215:
216: for (int i = 0; i < subDirs.length; i++) {
217: if (subDirs[i].isDirectory()) {
218: if (subDirs[i].getName().equalsIgnoreCase("CVS")) { //NOI18N
219: final File[] adminFiles = subDirs[i]
220: .listFiles();
221: for (int j = 0; j < adminFiles.length; j++) {
222: adminFiles[j].delete();
223: }
224: subDirs[i].delete();
225: } else {
226: deleteCVSSubDirs(subDirs[i]);
227: }
228: }
229: }
230: }
231: }
232:
233: public String getCVSCommand() {
234: StringBuffer toReturn = new StringBuffer("export "); //NOI18N
235: toReturn.append(getCVSArguments());
236: if (modules != null && modules.size() > 0) {
237: for (Iterator it = modules.iterator(); it.hasNext();) {
238: String module = (String) it.next();
239: toReturn.append(module);
240: toReturn.append(' ');
241: }
242: } else {
243: String localizedMsg = CommandException
244: .getLocalMessage("ExportCommand.moduleEmpty.text"); //NOI18N
245: toReturn.append(" "); //NOI18N
246: toReturn.append(localizedMsg);
247: }
248: return toReturn.toString();
249: }
250:
251: public String getCVSArguments() {
252: StringBuffer toReturn = new StringBuffer(""); //NOI18N
253: if (!isRecursive()) {
254: toReturn.append("-l "); //NOI18N
255: }
256: if (isUseHeadIfNotFound()) {
257: toReturn.append("-f "); //NOI18N
258: }
259: if (getExportByDate() != null) {
260: toReturn.append("-D "); //NOI18N
261: toReturn.append(getExportByDate());
262: toReturn.append(" "); //NOI18N
263: }
264: if (getExportByRevision() != null) {
265: toReturn.append("-r "); //NOI18N
266: toReturn.append(getExportByRevision());
267: toReturn.append(" "); //NOI18N
268: }
269: if (isPruneDirectories()) {
270: toReturn.append("-P "); //NOI18N
271: }
272: if (isNotShortenPaths()) {
273: toReturn.append("-N "); // NOI18N
274: }
275: if (isNotRunModuleProgram()) {
276: toReturn.append("-n "); // NOI18N
277: }
278: if (getExportDirectory() != null) {
279: toReturn.append("-d "); //NOI18N
280: toReturn.append(getExportDirectory());
281: toReturn.append(" "); //NOI18N
282: }
283: if (getKeywordSubstitutionOptions() != null) {
284: toReturn.append("-k"); //NOI18N
285: toReturn.append(getKeywordSubstitutionOptions().toString());
286: toReturn.append(" "); //NOI18N
287: }
288: return toReturn.toString();
289: }
290:
291: public boolean setCVSCommand(char opt, String optArg) {
292: if (opt == 'k') {
293: setKeywordSubstitutionOptions(KeywordSubstitutionOptions
294: .findKeywordSubstOption(optArg));
295: } else if (opt == 'r') {
296: setExportByRevision(optArg);
297: } else if (opt == 'f') {
298: setUseHeadIfNotFound(true);
299: } else if (opt == 'D') {
300: setExportByDate(optArg);
301: } else if (opt == 'd') {
302: setExportDirectory(optArg);
303: } else if (opt == 'P') {
304: setPruneDirectories(true);
305: } else if (opt == 'N') {
306: setNotShortenPaths(true);
307: } else if (opt == 'n') {
308: setNotRunModuleProgram(true);
309: } else if (opt == 'l') {
310: setRecursive(false);
311: } else if (opt == 'R') {
312: setRecursive(true);
313: } else {
314: return false;
315: }
316: return true;
317: }
318:
319: public void resetCVSCommand() {
320: setModules(null);
321: setKeywordSubstitutionOptions(null);
322: setPruneDirectories(false);
323: setRecursive(true);
324: setExportByDate(null);
325: setExportByRevision(null);
326: setExportDirectory(null);
327: setUseHeadIfNotFound(false);
328: setNotShortenPaths(false);
329: setNotRunModuleProgram(false);
330: }
331:
332: public String getOptString() {
333: return "k:r:D:NPlRnd:f"; //NOI18N
334: }
335:
336: /**
337: * Creates the ExportBuilder.
338: */
339: public Builder createBuilder(EventManager eventManager) {
340: return new ExportBuilder(eventManager, this );
341: }
342:
343: /**
344: * Called when the server wants to send a message to be displayed to
345: * the user. The message is only for information purposes and clients
346: * can choose to ignore these messages if they wish.
347: * @param e the event
348: */
349: public void messageSent(MessageEvent e) {
350: super .messageSent(e);
351: // we use this event to determine which directories need to be checked
352: // for updating
353: if (pruneDirectories && e.getMessage().indexOf(UPDATING) > 0) {
354: File file = new File(getLocalDirectory(), e.getMessage()
355: .substring(
356: e.getMessage().indexOf(UPDATING)
357: + UPDATING.length()));
358: emptyDirectories.add(file);
359: }
360: }
361:
362: /**
363: * Prunes a directory, recursively pruning its subdirectories
364: * @param directory the directory to prune
365: */
366: private boolean pruneEmptyDirectory(File directory)
367: throws IOException {
368: boolean empty = true;
369:
370: final File[] contents = directory.listFiles();
371:
372: // should never be null, but just in case...
373: if (contents != null) {
374: for (int i = 0; i < contents.length; i++) {
375: if (contents[i].isFile()) {
376: empty = false;
377: } else {
378: if (!contents[i].getName().equals("CVS")) { //NOI18N
379: empty = pruneEmptyDirectory(contents[i]);
380: }
381: }
382:
383: if (!empty) {
384: break;
385: }
386: }
387:
388: if (empty) {
389: // check this is a CVS directory and not some directory the user
390: // has stupidly called CVS...
391: final File entriesFile = new File(directory,
392: "CVS/Entries"); //NOI18N
393: if (entriesFile.exists()) {
394: final File adminDir = new File(directory, "CVS"); //NOI18N
395: final File[] adminFiles = adminDir.listFiles();
396: for (int i = 0; i < adminFiles.length; i++) {
397: adminFiles[i].delete();
398: }
399: adminDir.delete();
400: directory.delete();
401: }
402: }
403: }
404:
405: return empty;
406: }
407:
408: /**
409: * Remove any directories that don't contain any files
410: */
411: private void pruneEmptyDirectories() throws IOException {
412: final Iterator it = emptyDirectories.iterator();
413: while (it.hasNext()) {
414: final File dir = (File) it.next();
415: // we might have deleted it already (due to recursive delete)
416: // so we need to check existence
417: if (dir.exists()) {
418: pruneEmptyDirectory(dir);
419: }
420: }
421: emptyDirectories.clear();
422: }
423:
424: /** Getter for property exportByDate.
425: * @return Value of property exportByDate.
426: */
427: public String getExportByDate() {
428: return this .exportByDate;
429: }
430:
431: /** Setter for property exportByDate.
432: * @param exportByDate New value of property exportByDate.
433: */
434: public void setExportByDate(String exportByDate) {
435: this .exportByDate = exportByDate;
436: }
437:
438: /** Getter for property exportByRevision.
439: * @return Value of property exportByRevision.
440: */
441: public String getExportByRevision() {
442: return this .exportByRevision;
443: }
444:
445: /** Setter for property exportByRevision.
446: * @param exportByRevision New value of property exportByRevision.
447: */
448: public void setExportByRevision(String exportByRevision) {
449: this .exportByRevision = exportByRevision;
450: }
451:
452: /** Getter for property exportDirectory.
453: * @return Value of property exportDirectory.
454: */
455: public String getExportDirectory() {
456: return this .exportDirectory;
457: }
458:
459: /** Setter for property exportDirectory.
460: * @param exportDirectory New value of property exportDirectory.
461: */
462: public void setExportDirectory(String exportDirectory) {
463: this .exportDirectory = exportDirectory;
464: }
465:
466: /** Getter for property useHeadIfNotFound.
467: * @return Value of property useHeadIfNotFound.
468: */
469: public boolean isUseHeadIfNotFound() {
470: return this .useHeadIfNotFound;
471: }
472:
473: /** Setter for property useHeadIfNotFound.
474: * @param useHeadIfNotFound New value of property useHeadIfNotFound.
475: */
476: public void setUseHeadIfNotFound(boolean useHeadIfNotFound) {
477: this .useHeadIfNotFound = useHeadIfNotFound;
478: }
479:
480: /**
481: * Getter for property notShortenPaths.
482: * @return Value of property notShortenPaths.
483: */
484: public boolean isNotShortenPaths() {
485: return notShortenPaths;
486: }
487:
488: /**
489: * Setter for property notShortenPaths.
490: * @param notShortenPaths New value of property notShortenPaths.
491: */
492: public void setNotShortenPaths(boolean notShortenPaths) {
493: this .notShortenPaths = notShortenPaths;
494: }
495:
496: /**
497: * Getter for property notRunModuleProgram.
498: * @return Value of property notRunModuleProgram.
499: */
500: public boolean isNotRunModuleProgram() {
501: return notRunModuleProgram;
502: }
503:
504: /**
505: * Setter for property notRunModuleProgram.
506: * @param notRunModuleProgram New value of property notRunModuleProgram.
507: */
508: public void setNotRunModuleProgram(boolean notRunModuleProgram) {
509: this.notRunModuleProgram = notRunModuleProgram;
510: }
511:
512: }
|