source: trunk/gli/src/org/greenstone/gatherer/shell/GShell.java@ 8236

Last change on this file since 8236 was 8236, checked in by mdewsnip, 20 years ago

Replaced all Gatherer.print* with DebugStream.print*.

  • Property svn:keywords set to Author Date Id Revision
File size: 23.5 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37package org.greenstone.gatherer.shell;
38
39import java.io.*;
40import java.net.*;
41import java.util.ArrayList;
42import java.util.Enumeration;
43import javax.swing.*;
44import javax.swing.event.*;
45import javax.swing.tree.*;
46import org.greenstone.gatherer.Configuration;
47import org.greenstone.gatherer.DebugStream;
48import org.greenstone.gatherer.Dictionary;
49import org.greenstone.gatherer.Gatherer;
50import org.greenstone.gatherer.cdm.CollectionConfiguration;
51import org.greenstone.gatherer.cdm.CollectionDesignManager;
52import org.greenstone.gatherer.cdm.CollectionMetaManager;
53import org.greenstone.gatherer.cdm.CollectionMeta;
54import org.greenstone.gatherer.msm.GreenstoneArchiveParser;
55import org.greenstone.gatherer.shell.GShellListener;
56import org.greenstone.gatherer.shell.GShellProgressMonitor;
57import org.greenstone.gatherer.util.StaticStrings;
58import org.greenstone.gatherer.util.Utility;
59
60/** The <strong>GShell</strong> is reponsible for running a separately threaded process in the command shell. This is necessary for executing the Perl Scripts and also for other system related funcitonality.
61 */
62public class GShell
63 extends Thread {
64 /** A flag used to determine if this process has been asked to cancel. */
65 private boolean cancel = false;
66 /** The list of listeners associated with this class. */
67 private EventListenerList listeners = null;
68 /** The current status of this shell process. */
69 private int status = -1;
70 /** The type of message being sent. */
71 private int msg_type = -1;
72 /** The type of shell process. */
73 private int type = -1;
74 /** The caller of this process, and thus the class most interested in messages. */
75 private GShellListener caller = null;
76 /** The progress monitor associated with this process. */
77 private GShellProgressMonitor progress = null;
78 /** Arguments to be given to the process (including the executable you are calling. */
79 private String args[] = null;
80 /** Element in process type enumeration. */
81 static final public int BUILD = 0;
82 /** Element in process type enumeration. */
83 static final public int IMPORT = 1;
84 /** Element in process type enumeration. */
85 static final public int NEW = 2;
86 /** Element in process type enumeration. */
87 static final public int EXPORT = 3;
88 /** Element in process type enumeration. */
89 static final public int CONVERT = 4;
90 /** Element in process type enumeration. */
91 static final public int OTHER = 5;
92 /** Element in status type enumeration. */
93 static final public int ERROR = 0;
94 /** Element in status type enumeration. */
95 static final public int OK = 1;
96 static final public int CANCELLED = 2;
97 /** Element in process type name enumeration. */
98 static public String GSHELL_BUILD = "gshell_build";
99 /** Element in process type name enumeration. */
100 static public String GSHELL_IMPORT = "gshell_import";
101 /** Element in process type name enumeration. */
102 static public String GSHELL_NEW = "gshell_new";
103 /** Element in process type name enumeration */
104 static public String GSHELL_EXPORT = "gshell_export";
105 /** Element in process type name enumeration */
106 static public String GSHELL_CONVERT = "gshell_convert";
107
108 /** Determine if the given process is still executing. It does this by attempting to throw an exception - not the most efficient way, but the only one as far as I know
109 * @param process the Process to test
110 * @return true if it is still executing, false otherwise
111 */
112 static public boolean processRunning(Process process) {
113 boolean process_running = false;
114
115 try {
116 process.exitValue(); // This will throw an exception if the process hasn't ended yet.
117 }
118 catch(IllegalThreadStateException itse) {
119 process_running = true;
120 }
121 catch(Exception exception) {
122 DebugStream.printStackTrace(exception);
123 }
124 return process_running;
125 }
126
127 /** Constructor gatherer all the data required to create a new process, and emit meaningfull messages.
128 * @param args A <strong>String[]</strong> containing the arguments to the process thread, including the name of the executable.
129 * @param type An <strong>int</strong> that indicates what group of processes this process belongs to, as some are treated slightly differently (i.e an IMPORT type process is always followed by a BUILD one).
130 * @param msg_type As process threads may be background (like a makecol.pl call) or important processes in their own right (such as an IMPORT-BUILD) we must be able to set what level messages posted by this class will have by usings this <strong>int</strong>.
131 * @param caller The default <i>GShellListener</i> that is interested in the progress of this process.
132 * @param progress The <i>GShellProgressMonitor</i> associated with this process.
133 * @param name A <strong>String</strong> identifier given to the process, for convience and debug reasons.
134 */
135 public GShell(String args[], int type, int msg_type, GShellListener caller, GShellProgressMonitor progress, String name) {
136 super(name);
137 this.args = args;
138 this.msg_type = msg_type;
139 this.type = type;
140 this.caller = caller;
141 this.progress = progress;
142 this.status = 0;
143 // Lower this jobs priority
144 this.setPriority(Thread.MIN_PRIORITY);
145 listeners = new EventListenerList();
146 listeners.add(GShellListener.class, caller);
147 }
148 /** This method adds another shell listener to this process.
149 * @param listener The new <i>GShellListener</i>.
150 */
151 public void addGShellListener(GShellListener listener) {
152 listeners.add(GShellListener.class, listener);
153 }
154 /** This method removes a certain shell listener from this process.
155 * @param listener The <i>GShellListener</i> to be removed.
156 */
157 /* private void removeGShellListener(GShellListener listener) {
158 listeners.remove(GShellListener.class, listener);
159 } */
160
161
162
163 protected boolean got_stream_char(InputStreamReader isr, StringBuffer line_buffer,
164 BufferedOutputStream bos) throws IOException
165 {
166 // Hopefully this doesn't block if the process is trying to write to STDOUT/STDERR.
167
168 boolean input_status = false;
169
170 if(isr.ready()) {
171 input_status = true;
172 int c = isr.read();
173 if(c == '\n' || c == '\r') {
174 if(line_buffer.length() > 0) {
175 String line = line_buffer.toString();
176 DebugStream.println("* " + line + " *");
177 fireMessage(type, typeAsString(type) + "> " + line, status, bos);
178 line = null;
179 line_buffer = new StringBuffer();
180 }
181 }
182 else {
183 line_buffer.append((char)c);
184 }
185 }
186
187 return input_status;
188 }
189
190
191 protected StringBuffer get_stream_char(InputStreamReader isr, StringBuffer line_buffer,
192 BufferedOutputStream bos) throws IOException
193 {
194 int c = isr.read();
195 ///atherer.println("isr: '" + (char) c + "'");
196 if(c == '\n' || c == '\r') {
197 if(line_buffer.length() > 0) {
198 String line = line_buffer.toString();
199 // DebugStream.println("* " + line + " *");
200 fireMessage(type, typeAsString(type) + "> " + line, status, bos);
201 line_buffer = new StringBuffer();
202 }
203 }
204 else {
205 line_buffer.append((char)c);
206 }
207
208 return line_buffer;
209 }
210
211
212 protected int check_for_error(String line, StringBuffer error_list,
213 int error_count)
214 {
215 if (line.startsWith("ERROR:")) {
216 error_count++;
217 if (error_count<10) {
218 error_list.append(line);
219 }
220 else if (error_count==10) {
221 error_list.append("...\n");
222 }
223 }
224 return error_count;
225 }
226
227
228 protected void runRemote(String[] args, BufferedOutputStream bos)
229 {
230 try {
231
232 String perl_cmd = args[0];
233 int perl_cmd_root = perl_cmd.lastIndexOf(File.separator);
234
235 if (perl_cmd_root > 0) {
236 String perl_cmd_cut = perl_cmd.substring(perl_cmd_root+1);
237 perl_cmd = perl_cmd_cut;
238 }
239
240 String launch = Gatherer.cgiBase + "launch";
241 launch = launch + "?cmd=" + perl_cmd;
242
243 for(int i = 1; i<args.length; i++) {
244
245 String arg = args[i];
246
247 if (arg.equals("-collectdir") || arg.equals("-importdir")
248 || arg.equals("-builddir")) {
249 // skip it
250 i++;
251 continue;
252 }
253
254 launch += "&";
255
256 if(arg.startsWith(StaticStrings.MINUS_CHARACTER)) {
257 String name = arg.substring(1);
258 launch = launch + name + "=";
259 if (i+1<args.length-1) {
260 if (!args[i+1].startsWith(StaticStrings.MINUS_CHARACTER)) {
261 i++;
262 String val = URLEncoder.encode(args[i],"UTF-8");
263 launch = launch + val;
264 }
265 }
266 }
267 else {
268 launch = launch + "c=" + arg;
269 }
270 }
271
272 System.err.println("**** launch url = " + launch);
273 // fireMessage(type, Dictionary.get("GShell.Command") + ": " + launch, status, null); // ****
274
275 URL launch_url = new URL(launch);
276 URLConnection launch_connection = launch_url.openConnection();
277 InputStream stdis = launch_connection.getInputStream();
278 InputStreamReader stdisr = new InputStreamReader(stdis, "UTF-8");
279
280 BufferedReader stdbr = new BufferedReader(stdisr);
281
282 StringBuffer error_list = new StringBuffer();
283 int error_count = 0;
284
285 if (type == GShell.NEW) {
286 while(true) {
287 String line = stdbr.readLine();
288 if (line == null) { break; }
289 error_count = check_for_error(line,error_list,error_count);
290 }
291 }
292 else {
293 while(!hasSignalledStop()) {
294 String line = stdbr.readLine();
295 if (line == null) { break; }
296 error_count = check_for_error(line,error_list,error_count);
297 fireMessage(type, typeAsString(type) + "> " + line, status, bos);
298 }
299 }
300 stdbr.close();
301
302 if (error_count>0) {
303 status = ERROR;
304 System.err.println(error_list);
305 if (type != GShell.NEW) {
306 fireMessage(type, typeAsString(type) + "> " + error_list, status, null);
307 }
308 }
309 else {
310 status = OK;
311 fireMessage(type, typeAsString(type) + "> " + Dictionary.get("GShell.Success"), status, null);
312 }
313
314
315 }
316 // Exception
317 catch (Exception exception) {
318 System.err.println("Exception in GShell.runRemote() - unexpected");
319 DebugStream.printStackTrace(exception);
320 status = ERROR;
321 }
322 }
323
324
325 protected void runLocal(String[] args, BufferedOutputStream bos)
326 {
327 try {
328 String command = "";
329 for(int i = 0; i < args.length; i++) {
330 command = command + args[i] + " ";
331 }
332
333 DebugStream.println("Command: "+command);
334 ///ystem.err.println("Command: " + command);
335 fireMessage(type, Dictionary.get("GShell.Command") + ": " + command, status, null);
336
337 Runtime rt = Runtime.getRuntime();
338 Process prcs = rt.exec(args);
339
340 InputStreamReader eisr = new InputStreamReader( prcs.getErrorStream(), "UTF-8" );
341 InputStreamReader stdisr = new InputStreamReader( prcs.getInputStream(), "UTF-8" );
342
343 StringBuffer eline_buffer = new StringBuffer();
344 StringBuffer stdline_buffer = new StringBuffer();
345
346 while(type != GShell.NEW && processRunning(prcs) && !hasSignalledStop()) {
347 // Hopefully this doesn't block if the process is trying to write to STDOUT.
348 if((eisr!=null) && eisr.ready()) {
349 eline_buffer = get_stream_char(eisr,eline_buffer,bos);
350 }
351 // Hopefully this won't block if the process is trying to write to STDERR
352 else if(stdisr.ready()) {
353 stdline_buffer = get_stream_char(stdisr,stdline_buffer,bos);
354 }
355 else {
356 try {
357 sleep(100);
358 }
359 catch(Exception exception) {
360 }
361 }
362 }
363
364 if(!hasSignalledStop()) {
365 // Of course, just because the process is finished doesn't
366 // mean the incoming streams are empty. Unfortunately I've
367 // got no chance of preserving order, so I'll process the
368 // error stream first, then the out stream
369 while(eisr.ready()) {
370 eline_buffer = get_stream_char(eisr,eline_buffer,bos);
371 }
372
373 while(stdisr.ready()) {
374 stdline_buffer = get_stream_char(stdisr,stdline_buffer,bos);
375 }
376
377 // Ensure that any messages still remaining in the string buffers are fired off.
378 if(eline_buffer.length() > 0) {
379 String eline = eline_buffer.toString();
380 //DebugStream.println("Last bit of eline: " + eline);
381 fireMessage(type, typeAsString(type) + "> " + eline, status, bos);
382 eline = null;
383 }
384
385 if(stdline_buffer.length() > 0) {
386 String stdline = stdline_buffer.toString();
387 //DebugStream.println("Last bit of stdline: " + stdline);
388 fireMessage(type, typeAsString(type) + "> " + stdline, status, null);
389 stdline = null;
390 }
391 }
392 else {
393 System.err.println("We've been asked to stop.");
394 }
395
396
397 if(!hasSignalledStop()) {
398 // Now display final message based on exit value
399
400 prcs.waitFor();
401
402 if(prcs.exitValue() == 0) {
403 status = OK;
404 fireMessage(type, typeAsString(type) + "> " + Dictionary.get("GShell.Success"), status, null);
405 }
406 else {
407 status = ERROR;
408 fireMessage(type, typeAsString(type) + "> " + Dictionary.get("GShell.Failure"), status, null);
409 }
410
411 eisr.close();
412 stdisr.close();
413 }
414 else {
415 // I need to somehow kill the child process. Unfortunately
416 // Thread.stop() and Process.destroy() both fail to do
417 // this. But now, thankx to the magic of Michaels 'close the
418 // stream suggestion', it works fine (no it doesn't!)
419 prcs.getInputStream().close();
420 prcs.getErrorStream().close();
421 prcs.getOutputStream().close();
422 prcs.destroy();
423 status = CANCELLED;
424 }
425 }
426 // Exception
427 catch (Exception exception) {
428 DebugStream.println("Exception in GShell.runLocal() - unexpected");
429 DebugStream.printStackTrace(exception);
430 status = ERROR;
431 }
432 }
433
434
435
436 /** Any threaded class must include this method to allow the thread body to be run. */
437 public void run() {
438
439 String col_name = args[args.length-1];
440
441 // Determine if the user has asked for an outfile.
442 String out_name = null;
443 BufferedOutputStream bos = null;
444 if(type == IMPORT || type == BUILD) {
445 if(type == IMPORT) {
446 out_name = (String) Gatherer.c_man.getCollection().build_options.getImportValue("out");
447 }
448 else {
449 out_name = (String) Gatherer.c_man.getCollection().build_options.getBuildValue("out");
450 }
451 if(out_name != null && out_name.length() > 0) {
452 try {
453 bos = new BufferedOutputStream(new FileOutputStream(new File(out_name), true));
454 }
455 catch (Exception error) {
456 DebugStream.printStackTrace(error);
457 }
458 }
459 }
460
461 if (Gatherer.isGsdlRemote) {
462 if (type == IMPORT) {
463 if (progress!=null) {
464 progress.messageOnProgressBar("Uploading data to server");
465 }
466
467 // zip up import folder
468 Utility.zipup(col_name,"import");
469 // upload it to gsdl server
470 Utility.upload_url_zip(col_name,"import");
471
472 // upload etc folder to server (need collect.cfg and any hfiles)
473 Utility.zipup(col_name, "etc");
474 Utility.upload_url_zip(col_name,"etc");
475
476 String col_dir = Utility.getCollectionDir(Configuration.gsdl_path, col_name);
477 File img_dir = new File(Utility.getImagesDir(col_dir));
478 if (img_dir.exists()) {
479 // upload images/ directory to server
480 Utility.zipup(col_name,"images");
481 Utility.upload_url_zip(col_name,"images");
482 }
483
484 // see if collection specific image needs uploading
485 CollectionMetaManager cmm
486 = CollectionDesignManager.collectionmeta_manager;
487
488 CollectionMeta icon_collection_collectionmeta
489 = cmm.getMetadatum(CollectionConfiguration.COLLECTIONMETADATA_ICONCOLLECTION_STR);
490 CollectionMeta icon_collection_small_collectionmeta
491 = cmm.getMetadatum(CollectionConfiguration.COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR);
492
493 String ics_text = icon_collection_collectionmeta.getValue(CollectionMeta.TEXT);
494 String icsc_text = icon_collection_small_collectionmeta.getValue(CollectionMeta.TEXT);
495 if ((ics_text != null) && (ics_text != "")) {
496 // Stub code for detecting when collectoin image changed
497 // => need to upload images
498 // System.err.println("**** ics_text = " + ics_text);
499 }
500
501
502 if (progress!=null) {
503 progress.messageOnProgressBar("");
504 }
505
506 }
507 }
508
509
510 // Issue a processBegun event
511 fireProcessBegun(type, status);
512 if (Gatherer.isGsdlRemote) {
513 runRemote(args,bos);
514 }
515 else {
516 runLocal(args,bos);
517 }
518
519 if(status == OK) {
520 if (type == NEW) {
521 if (Gatherer.isGsdlRemote) {
522 Utility.download_url_zip(col_name,".");
523 Utility.unzip(col_name);
524 }
525 }
526 else if(type == IMPORT) {
527
528 // download the archives directory (if gsdl server is remote)
529 if (Gatherer.isGsdlRemote) {
530
531 if (progress!=null) {
532 progress.messageOnProgressBar("Downloading archive data from server");
533 }
534
535
536 Utility.delete(Gatherer.c_man.getCollectionArchive()); // remove current archives
537 Utility.download_url_zip(col_name,"archives");
538 Utility.unzip(col_name);
539
540 if (progress!=null) {
541 progress.messageOnProgressBar("");
542 }
543 }
544
545 // extract any new metadata from the archive directory.
546 fireMessage(type, typeAsString(type) + "> " + Dictionary.get("GShell.Parsing_Metadata_Start"), status, null);
547 new GreenstoneArchiveParser(progress, this);
548 fireMessage(type, typeAsString(type) + "> " + Dictionary.get("GShell.Parsing_Metadata_Complete"), status, null);
549 }
550
551 else if(type == BUILD) {
552
553 // download the building directory (if gsdl server is remote)
554 if (Gatherer.isGsdlRemote) {
555 if (progress!=null) {
556 progress.messageOnProgressBar("Downloading index data from server");
557 }
558
559 Utility.delete(Gatherer.c_man.getCollectionBuild()); // remove current build dir
560 Utility.download_url_zip(col_name,"building");
561 Utility.unzip(col_name);
562
563 if (progress!=null) {
564 progress.messageOnProgressBar("");
565 }
566
567 }
568
569 }
570 }
571
572 if(hasSignalledStop()) {
573 status = CANCELLED;
574 }
575 // We're done.
576 fireProcessComplete(type, status);
577 // Close bos
578 if(bos != null) {
579 try {
580 bos.close();
581 bos = null;
582 }
583 catch(Exception error) {
584 DebugStream.printStackTrace(error);
585 }
586 }
587 }
588 /** Method for firing a message to all interested listeners.
589 * @param type An <strong>int</strong> indicating the process type.
590 * @param message The message as a <strong>String</strong>.
591 * @param status An <strong>int</strong> specifying the current status of the process.
592 */
593 public void fireMessage(int type, String message, int status, BufferedOutputStream bos) {
594 GShellEvent event = new GShellEvent(this, 0, type, message, status);
595 // If there is a progress monitor attached, pass the event to it first. Note that we pass a queue of messages as the processing may cause one message to be split into several.
596 ArrayList message_queue = new ArrayList();
597 message_queue.add(event);
598 if(progress != null) {
599 progress.process(message_queue);
600 }
601 for(int j = 0; j < message_queue.size(); j++) {
602 GShellEvent current_event = (GShellEvent) message_queue.get(j);
603 // If the event hasn't been vetoed, pass it on to other listeners
604 if(!current_event.isVetoed()) {
605 Object[] concerned = listeners.getListenerList();
606 for(int i = 0; i < concerned.length ; i++) {
607 if(concerned[i] == GShellListener.class) {
608 ((GShellListener)concerned[i+1]).message(current_event);
609 }
610 }
611 concerned = null;
612 }
613 }
614 // And if we have a buffered output stream from error messages, send the message there
615 if(bos != null) {
616 try {
617 bos.write(message.getBytes(), 0, message.length());
618 }
619 catch(Exception exception) {
620 DebugStream.println("Exception in GShell.fireMessage() - unexpected");
621 DebugStream.printStackTrace(exception);
622 }
623 }
624 message_queue = null;
625 event = null;
626 }
627
628 /** Method for firing a process begun event which is called, strangly enough, when the process begins.
629 * @param type An <strong>int</strong> indicating the process type.
630 * @param status An <strong>int</strong> specifying the current status of the process.
631 */
632 protected void fireProcessBegun(int type, int status) {
633 // Start the progres monitor if available
634 if(progress != null) {
635 progress.start();
636 }
637 // Fire an event
638 GShellEvent event = new GShellEvent(this, 0, type, "", status);
639 Object[] concerned = listeners.getListenerList();
640 for(int i = 0; i < concerned.length ; i++) {
641 if(concerned[i] == GShellListener.class) {
642 ((GShellListener)concerned[i+1]).processBegun(event);
643 }
644 }
645 }
646 /** Method for firing a process complete event which is called, no surprise here, when the process ends.
647 * @param type An <strong>int</strong> indicating the process type.
648 * @param status An <strong>int</strong> specifying the current status of the process.
649 */
650 protected void fireProcessComplete(int type, int status) {
651 // Tidy up by stopping the progress bar. If it was cancelled then the cancel command has arrived via the progress bars and they don't need to be told again (it actually causes problems).
652 if(progress != null && status != CANCELLED) {
653 progress.stop();
654 }
655
656 // If we were cancelled, and we are lower details modes, fire off one last message.
657 if(status == CANCELLED && Configuration.getMode() <= Configuration.SYSTEMS_MODE) {
658 GShellEvent current_event = new GShellEvent(this, 0, type, Dictionary.get("GShell.Build.BuildCancelled"), status);
659 Object[] concerned = listeners.getListenerList();
660 for(int i = 0; i < concerned.length ; i++) {
661 if(concerned[i] == GShellListener.class) {
662 ((GShellListener)concerned[i+1]).message(current_event);
663 }
664 }
665 concerned = null;
666 }
667 // And firing off an event
668 GShellEvent event = new GShellEvent(this, 0, type, "", status);
669 Object[] concerned = listeners.getListenerList();
670 for(int i = 0; i < concerned.length ; i++) {
671 if(concerned[i] == GShellListener.class) {
672 ((GShellListener)concerned[i+1]).processComplete(event);
673 }
674 }
675 }
676
677 /** Method to determine if the user, via the progress monitor, has signalled stop.
678 * @return A <strong>boolean</strong> indicating if the user wanted to stop.
679 */
680 private boolean hasSignalledStop() {
681 boolean has_signalled_stop = false;
682 if(progress != null) {
683 has_signalled_stop = progress.hasSignalledStop();
684 }
685 return has_signalled_stop;
686 }
687
688 /** Converts a type into a text representation.
689 * @param type An <strong>int</strong> which maps to a shell process type.
690 * @return A <strong>String</strong> which is the thread process's text name.
691 */
692 public String typeAsString(int type) {
693 String name = null;
694 switch(type) {
695 case BUILD:
696 name = Dictionary.get("GShell.Build");
697 break;
698 case IMPORT:
699 name = Dictionary.get("GShell.Import");
700 break;
701 case NEW:
702 name = Dictionary.get("GShell.New");
703 break;
704 case EXPORT:
705 name= Dictionary.get("GShell.Export");
706 break;
707 case CONVERT:
708 name = Dictionary.get("GShell.Convert");
709 break;
710 default:
711 name = Dictionary.get("GShell.Other");
712 }
713 return name;
714 }
715}
Note: See TracBrowser for help on using the repository browser.