1   package demoRunner;
2   
3   
4   import java.io.BufferedReader;
5   import java.io.File;
6   import java.io.FileInputStream;
7   import java.io.FileNotFoundException;
8   import java.io.FilenameFilter;
9   import java.io.IOException;
10  import java.io.InputStreamReader;
11  import java.lang.reflect.InvocationTargetException;
12  import java.lang.reflect.Method;
13  import java.net.URL;
14  import java.util.ArrayList;
15  import java.util.Comparator;
16  import java.util.Iterator;
17  import java.util.List;
18  import java.util.jar.JarEntry;
19  import java.util.jar.JarInputStream;
20  import java.util.regex.Matcher;
21  import java.util.regex.Pattern;
22  
23  
24  /**
25   * Ajaa valitun paketin demoja
26   * @author ji
27   * @author vesal
28   *
29   */
30  public class DemoRunner {
31  
32  
33      /**
34       * Ajaa demon valitusta paketista, mutta ei aja kiellettyä luokkaa
35       * @param args          komentorivin argumentit
36       * @param packageName   paketin nimi josta etsitään luokkia
37       * @param noRunName     kielletyn luokan nimi
38       */
39      public static void run(String args[],String packageName, String noRunName) {
40          if (args.length > 1) {
41              System.out
42                      .println("Anna parametrina vain yhden ajettavan demon nimi.\n"
43                              + "Jos et anna parametreja, voit valita ajettavan demon "
44                              + "listasta.");
45              System.exit(1);
46          }
47  
48          
49          DemoRunner demo = new DemoRunner(packageName,noRunName);
50  
51          String msg = "";
52          if (args.length == 0) {
53              msg = demo.runInteractively();
54          } else {
55              msg = demo.runWithArgs(args);
56          }
57          System.out.println(msg);
58      }
59  
60      private ClassList classes;
61  
62      /**
63       * Alustaa ajaja-luokan
64       * @param packageName paketin nimi josta ajettavia etsitään
65       * @param noRunName mitä luokkia ei saa ajaa.
66       */
67      public DemoRunner(String packageName, String noRunName) {
68          classes = new ClassList(packageName,noRunName);
69          classes.sort();
70      }
71  
72      /**
73       * Ajaetaan demo argumenttien perusteella
74       * @param args merkijonotaulukko jossa ekana demon nimi ja sitten sen parametrit
75       * @return virheilmoitus
76       */
77      public String runWithArgs(String[] args) {
78          String sampleName = args[0];
79          //String[] restOfArgs = java.util.Arrays
80          //      .copyOfRange(args, 1, args.length);      // Ei löydy JDK <1.6:sta.
81          String[] restOfArgs = new String[args.length-1];
82          for (int i = 0; i<args.length-1; ++i) {
83              restOfArgs[i] = args[i+1];
84          }
85          Class<?> sampleClass = classes.get(sampleName);
86          if (sampleClass == null)
87              return "Demoa " + sampleName + " ei ole.";
88          runDemo(sampleClass, restOfArgs);
89          return "";
90      }
91  
92      /**
93       * Ajetaan demo kysymällä käyttäjältä ajettavan luokan nimi
94       * @return virheilmoitus
95       */
96      public String runInteractively() {
97          String resultMessage = "";
98  
99          System.out.println("Valitse ajettava demo");
100 
101         int number = 0;
102         for (Class<?> aClass : classes) {
103             System.out.printf("  %d. %s\n", number++, aClass.getName());
104         }
105 
106         BufferedReader consoleInputStream = new BufferedReader(
107                 new InputStreamReader(System.in));
108 
109         while (true) {
110             try {
111                 System.out.print("Anna ajettavan demon numero: ");
112                 String response = consoleInputStream.readLine();
113                 if (response.equals("")) {
114                     break;
115                 }
116                 int index = Integer.parseInt(response);
117                 Class<?> sampleClass = classes.get(index);
118                 runDemo(sampleClass, null);
119                 resultMessage = "";
120                 break;
121             } catch (IOException e) {
122                 resultMessage = "Demoa ei voitu ajaa: "
123                         + e.getLocalizedMessage();
124                 System.err.println(resultMessage);
125             } catch (NumberFormatException e) {
126                 System.out.println("Anna numero 0..." + (classes.size() - 1));
127                 resultMessage = "";
128             } catch (IndexOutOfBoundsException e) {
129                 System.out.println("Anna numero 0..." + (classes.size() - 1));
130                 resultMessage = "";
131             }
132         }
133 
134         return resultMessage;
135     }
136 
137     private void runDemo(Class<?> sampleClass, String[] args) {
138         try {
139             Object objectParameters[] = { new String[] {} };
140             Class<?> classParameters[] = { objectParameters[0].getClass() };
141             Method mainMethod = sampleClass.getDeclaredMethod("main",
142                     classParameters);
143             mainMethod.invoke(null, (Object) args);
144         } catch (SecurityException e) { // Virheet sivuutetaan, sillä
145             e.printStackTrace(System.err); // niistä ei ole haittaa.
146         } catch (IllegalArgumentException e) { // Tulostetaan ne kuitenkin
147             e.printStackTrace(System.err); // virhevirtaan.
148         } catch (NoSuchMethodException e) {
149             e.printStackTrace(System.err);
150         } catch (IllegalAccessException e) {
151             e.printStackTrace(System.err);
152         } catch (InvocationTargetException e) {
153             e.printStackTrace(System.err);
154         }
155     }
156     
157     
158     /**
159      * Luokka ajettavista demoista 
160      * @author ji
161      */
162     public static class ClassList implements Iterable<Class<?>> {
163         private List<Class<?>> classes;
164         private String packageOfClasses;
165         private String noRunName;
166 
167         /**
168          * Tutkitaan onko tiedosto sallittu tiedosto.
169          * @param name      tiedoston nimi
170          * @param noRunName1 nimi joka ei saa olla
171          * @return true jos nimi on sallittu
172          */
173         private boolean isAllowed(String name,String noRunName1) {
174             return !name.endsWith(noRunName1+".class");
175         }
176         
177 
178         /**
179          * Alustetaan ajettavien luokkien lista
180          * @param packageName paketti josta ajettavia etsitään
181          * @param noRunName luokka jota ei saa ajaa
182          */
183         public ClassList(String packageName,String noRunName) {
184             classes = new ArrayList<Class<?>>();
185             add(packageName, noRunName);
186         }
187         
188         /**
189          * Lisätään ajettavien demojen joukkoon uusia demoja
190          * @param packageName paketti josta luokkia estitään
191          * @param noRunName1 nimi joita ei saa lisätä
192          */
193         public final void add(String packageName,String noRunName1) {
194             String thisPackageName = DemoRunner.class.getPackage().getName();
195             String thisName = DemoRunner.class.getSimpleName();
196             this.noRunName = noRunName1;
197             packageOfClasses = packageName;
198             URL demoURL = DemoRunner.class.getResource(thisName+".class");
199             File classFile = new File(demoURL.toExternalForm());
200             String pathnameOfClass = classFile.getParentFile().getPath();
201 
202             String[] packagePath = pathnameOfClass.split("!")[0].split(":");
203             String thisPackage = packagePath[packagePath.length - 1];
204             String packageRoot = thisPackage.substring(0,thisPackage.length()-thisPackageName.length());
205             
206             if (packagePath[0].equals("jar")/*packageFile.isFile()*/) {
207                 String packageFileName = thisPackage; 
208                 File packageFile = new File(packageFileName);
209                 classesInJar(packageFile);
210             } else /* if (fff.isDirectory()) */{
211                 String packageFileName = packageRoot+File.separator+packageName;
212                 File packageFile = new File(packageFileName);
213                 classesInDirectory(packageFile);
214             }
215         }
216 
217         private void classesInDirectory(final File directory) {
218             File[] files = directory.listFiles(new FilenameFilter() {
219                 @SuppressWarnings("synthetic-access")
220                 @Override
221                 public boolean accept(File dir, String name) {
222                     return dir.equals(directory) && name.endsWith(".class")
223                             && !name.contains("$") && isAllowed(name, noRunName);
224                 }
225             });
226             for (File file : files) {
227                 loadSampleClass(file);
228             }
229         }
230 
231         
232         private void classesInJar(File jarFile) {
233             JarInputStream jarStream = null;
234             try {
235                 jarStream = new JarInputStream(new FileInputStream(jarFile));
236                 JarEntry jarEntry = jarStream.getNextJarEntry();
237                 while (jarEntry != null) {
238                     if (jarEntry.toString().endsWith(".class") && isAllowed(jarEntry.toString(),noRunName) ) {
239                         
240                         Pattern pp = Pattern.compile(packageOfClasses
241                                 + "/([\\w\\d_]+)\\.class");
242                         Matcher mm = pp.matcher(jarEntry.toString());
243                         if (mm.matches()) {
244                             loadSampleClass(mm.group(1));
245                         }
246                     }
247                     try {
248                         jarEntry = jarStream.getNextJarEntry();
249                     } catch (IOException e) {
250                         // Ei välitetä tästä, vaan yritetään uudelleen
251                     }
252                 }
253             } catch (FileNotFoundException e) { // Virhetilanteessa palautuu tyhjä
254                 // lista
255                 e.printStackTrace(System.err); // tai luetut alkiot, joten ei
256                 // välitetä poikkeutuksista.
257             } catch (IOException e) { // Tulostetaan ne kuitenkin talteen
258                 // virhevirtaan.
259                 e.printStackTrace(System.err);
260             } finally {
261                 if ( jarStream != null )
262                     try {
263                         jarStream.close();
264                     } catch (IOException e) { // mitätpä sitä tehtäisiin
265                     }
266             }
267         }
268 
269 
270         /**
271          * Returns an iterator over the classes in this list in proper sequence.
272          * @return an iterator over the elements in proper sequence
273          */
274         @Override
275         public Iterator<Class<?>> iterator() {
276             return classes.iterator();
277         }
278 
279         /**
280          * Lajittelee luokat järjestykseen nimen perusteella
281          */
282         public void sort() {
283             java.util.Collections.sort(classes, new Comparator<Class<?>>() {
284                 @Override
285                 public int compare(Class<?> o1, Class<?> o2) {
286                     return o1.getName().compareTo(o2.getName());
287                 }
288             });
289         }
290 
291         /**
292          * Finds a sample class by name.
293          * 
294          * @param sampleName
295          *            name of sample class to be found.
296          * @return sample class, or null if not found.
297          */
298         public Class<?> get(String sampleName) {
299             for (Class<?> aClass : this) {
300                 if (aClass.getName().endsWith(sampleName)) {
301                     return aClass;
302                 }
303             }
304             return null;
305         }
306 
307         /**
308          * Finds a Sample class by index.
309          * 
310          * @param index
311          *            index of a Sample class to return
312          * @return a Sample class, or null if not found
313          * @exception IndexOutOfBoundsException,
314          *                if the index is out of range.
315          */
316         public Class<?> get(int index) throws IndexOutOfBoundsException {
317             return classes.get(index);
318         }
319 
320         /**
321          * The number of classes in this list, or Integer.MAX_VALUE, if the list
322          * contains more than Integer.MAX_VALUE classes.
323          * 
324          * @return the number of elements in the class list
325          */
326         public int size() {
327             return classes.size();
328         }
329 
330         private Class<?> loadSampleClass(File file) {
331             String filename = file.getName();
332             return loadSampleClass(filename.substring(0, filename
333                     .lastIndexOf(".")));
334         }
335 
336         private Class<?> loadSampleClass(String fname) {
337             Class<?> candidateClass = null;
338 
339             try {
340                 candidateClass = java.lang.Class.forName(packageOfClasses
341                         + "." + fname, true, ClassLoader.getSystemClassLoader());
342             } catch (ClassNotFoundException e) {
343                 return null;
344             }
345             classes.add(candidateClass);
346             return candidateClass;
347         }
348 
349     }
350     
351 }
352 
353