View Javadoc

1   package org.molwind.util;
2   
3   /*
4    * This file is part of Molwind.
5    *
6    * Molwind is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * Molwind is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with Molwind. If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.lang.reflect.Constructor;
23  import java.lang.reflect.InvocationTargetException;
24  import java.lang.reflect.Method;
25  import java.math.BigDecimal;
26  import java.sql.Timestamp;
27  import java.util.ArrayList;
28  import java.util.Enumeration;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Observable;
32  import java.util.Properties;
33  import java.util.Stack;
34  
35  import javax.servlet.ServletContext;
36  
37  import org.apache.commons.configuration.Configuration;
38  import org.apache.commons.configuration.ConfigurationException;
39  import org.apache.commons.configuration.XMLConfiguration;
40  
41  //import org.molwind.io.SDFileLocator;
42  import org.molwind.io.BasePathSupport;
43  import org.molwind.io.WorldLocator;
44  import org.molwind.model.MolwindWorld;
45  
46  /**
47   * Holds the configuration options of the molwind server. This is a singleton as
48   * there is only one per server.
49   * <p>
50   *
51   * @author <a href="mailto:oliver.karch@molwind.org">Oliver Karch</a>
52   * @version 1.0
53   */
54  public final class MolwindServerConfiguration extends Observable
55  implements WorldLocator, Configurator {
56  
57  //    private ArrayList<String> al = new ArrayList<String>();
58  //    private static ArrayList<String> url=
59  //        new ArrayList<String>(), urlXML=new ArrayList<String>();
60  //    private static HashMap<String, Integer>names=
61  //        new HashMap<String, Integer>();
62  //    private static HashMap<Integer,String>names2=
63  //        new HashMap<Integer,String>();
64  //    private static ArrayList<String>features=new ArrayList<String>();
65  //    private static worldCount=1;
66  //    private static String server;
67  //    private String confPath;
68  //    private static HashMap<Integer, Integer> isStarted=
69  //        new HashMap<Integer,Integer>();
70  
71      private String path;
72      private int moleculesPerLevel;
73      private long lastModified;
74      private double zoom;
75      private boolean intermediateLayer;
76      private ArrayList<String>  worldPaths;
77      private ArrayList worldLocators;
78      private Stack configStack;
79      private ServletContext servletContext;
80  
81      private static MolwindServerConfiguration config;
82  
83      /**
84       * Default maximum number of molecules per level.
85       */
86      public static final int DEFAULT_MOLECULES_PER_LEVEL = 180;
87  
88      /**
89       * Default zoom factor.
90       */
91      public static final double DEFAULT_ZOOM = 0.5d;
92  
93      /**
94       * Default intermediate layer flag.
95       */
96      public static final boolean DEFAULT_INTERMEDIATE_LAYER = false;
97  
98  
99      private MolwindServerConfiguration() {
100         this(null);
101     }
102 
103     private MolwindServerConfiguration(final String newPath) {
104         super();
105         path = newPath;
106         moleculesPerLevel = DEFAULT_MOLECULES_PER_LEVEL;
107         zoom = DEFAULT_ZOOM;
108         lastModified = 0L;
109         configStack = new Stack();
110         worldPaths = new ArrayList<String>();
111         worldLocators = new ArrayList();
112         initWorldPaths();
113 	
114     }
115 
116     private void initWorldPaths() {
117         String userPath = System.getProperty("user.dir");
118         if (userPath != null) {
119             addWorldPath(userPath);
120         }
121 
122         String userHomePath = System.getProperty("user.home");
123         if (userHomePath != null) {
124             addWorldPath(userHomePath);
125         }
126 
127         String javaPath = System.getProperty("java.class.path");
128         String[] javaPathParts = javaPath.split(File.pathSeparator);
129 
130         for (int i = 0; i < javaPathParts.length; i++) {
131             File theFile = new File(javaPathParts[i]);
132 
133             if (theFile.isDirectory()) {
134                 addWorldPath(javaPathParts[i]);
135             } else {
136                 theFile = theFile.getParentFile();
137                 if (theFile != null) {
138                     addWorldPath(theFile.getPath());
139                 }
140             }
141         }
142     }
143 
144 
145     /**
146      * Creates a new configuration object and initializes it with default
147      * values (lazily loaded).
148      *
149      * @param path
150      *      the path to the configuration file
151      * @return
152      *      the configuration instance
153      */
154     public static MolwindServerConfiguration getInstance(final String path) {
155         if (config == null) {
156             config = new MolwindServerConfiguration(path);
157         }
158         return config;
159     }
160 
161     /**
162      * Creates a new configuration object and initializes it with default
163      * values (lazily loaded).
164      *
165      * @return
166      *      the configuration instance
167      */
168     public static MolwindServerConfiguration getInstance() {
169         return getInstance(null);
170     }
171 
172 
173     private void pushConfig(final Configuration cfg, final String startKey)
174     throws ConfigurationException {
175         Iterator pit = cfg.getKeys(startKey);
176         Properties properties = new Properties();
177         configStack.push(properties);
178 
179         while (pit.hasNext()) {
180             String kn = (String) pit.next();
181             String an = Stringx.after(kn, startKey + ".");
182             String kv = cfg.getString(kn);
183 
184             if (kv != null) {
185                 properties.put(an, kv);
186             }
187         }
188     }
189 
190     private void load()
191     throws IOException {
192         File theFile = null;
193 
194         if (path != null) {
195             theFile = new File(path);
196 
197             if (theFile.exists() && (theFile.lastModified() > lastModified)) {
198                 MolwindLogger.info("Loading config from " + theFile);
199 
200                 try {
201                     XMLConfiguration xmlConfig = new XMLConfiguration(theFile);
202 
203                     setMoleculesPerLevel(xmlConfig.getInt("moleculesPerLevel",
204                             DEFAULT_MOLECULES_PER_LEVEL));
205                     setZoom(xmlConfig.getDouble("zoom", DEFAULT_ZOOM));
206                     setIntermediateLayer(xmlConfig.getBoolean(
207                             "intermediateLayer", DEFAULT_INTERMEDIATE_LAYER));
208 
209                     List list = xmlConfig.getList("locators.locator.class");
210 		    
211                     Iterator it = list.iterator();
212                     clearWorldLocators();
213 
214                     for (int i = 0; it.hasNext(); i++) {
215                         String className = (String) it.next();
216 			String pathNames= xmlConfig.getString("locators.locator.params.pathNames");
217 
218                         try {
219                             Class clazz = Class.forName(className);
220                             WorldLocator locator =
221                                 (WorldLocator) clazz.newInstance();
222 
223                             if (locator instanceof Configurable) {
224                                 pushConfig(xmlConfig,
225                                         "locators.locator(" + i + ").params");
226                                 try {
227                                     ((Configurable) locator).configure(this);
228                                 } catch (ConfigException ce) {
229                                     throw new IOException(ce.getMessage());
230                                 }
231                             }
232 			     if( locator instanceof BasePathSupport ) {
233 			     BasePathSupport bps = (BasePathSupport)locator;
234 			     for(String s: worldPaths ){
235 			       bps.addBasePath( s );
236 			     }
237 
238 			     }
239 			     /** if(locator instanceof SDFileLocator){
240 			      * SDFileLocator sdfl = (SDFileLocator)locator;
241 			      * sdfl.setWorldPaths(pathNames);
242 			      *}
243 			      */
244 
245                             addWorldLocator(locator);
246                         } catch (ClassNotFoundException cnfe) {
247                             MolwindLogger.warn("Cannot find locator "
248                                     + className, cnfe);
249                         } catch (InstantiationException iex) {
250                             MolwindLogger.warn("Cannot instantiate "
251                                     + className, iex);
252                         } catch (IllegalAccessException iax) {
253                             MolwindLogger.warn("Cannot instantiate "
254                                     + className, iax);
255                         }
256                     }
257                     lastModified = theFile.lastModified();
258                 } catch (ConfigurationException ce) {
259                     throw new IOException(ce.getMessage());
260                 }
261             }
262         }
263     }
264 
265     private void ensureLoad() {
266         try {
267             load();
268         } catch (IOException ioe) {
269             MolwindLogger.warn(ioe);
270         }
271     }
272 
273 
274     /**
275      * Get the Path value.
276      *
277      * @return
278      *      the Path value
279      */
280     public String getPath() {
281         ensureLoad();
282         return path;
283     }
284 
285     /**
286      * Set the Path value.
287      *
288      * @param newPath
289      *      the new Path value
290      */
291     public void setPath(final String newPath) {
292         this.path = newPath;
293         lastModified = 0L;
294     }
295 
296     /**
297      * Get the MoleculesPerLevel value.
298      *
299      * @return
300      *      the MoleculesPerLevel value
301      */
302     public int getMoleculesPerLevel() {
303         ensureLoad();
304         return moleculesPerLevel;
305     }
306 
307     /**
308      * Set the MoleculesPerLevel value.
309      *
310      * @param newMoleculesPerLevel
311      *      the new MoleculesPerLevel value
312      */
313     public void setMoleculesPerLevel(final int newMoleculesPerLevel) {
314         this.moleculesPerLevel = newMoleculesPerLevel;
315     }
316 
317     /**
318      * Get the Zoom value.
319      *
320      * @return
321      *      the Zoom value
322      */
323     public double getZoom() {
324         ensureLoad();
325         return zoom;
326     }
327 
328     /**
329      * Set the Zoom value.
330      *
331      * @param newZoom
332      *      the new Zoom value
333      */
334     public void setZoom(final double newZoom) {
335         this.zoom = newZoom;
336     }
337 
338     /**
339      * Get the IntermediateLayer value.
340      *
341      * @return
342      *      the IntermediateLayer value
343      */
344     public boolean isIntermediateLayer() {
345         ensureLoad();
346         return intermediateLayer;
347     }
348 
349     /**
350      * Set the IntermediateLayer value.
351      *
352      * @param newIntermediateLayer
353      *      the new IntermediateLayer value
354      */
355     public void setIntermediateLayer(final boolean newIntermediateLayer) {
356         this.intermediateLayer = newIntermediateLayer;
357     }
358 
359     /**
360      * Adds a search path for world data to locate.
361      *
362      * @param searchPath
363      *      a path where world data can be found
364      */
365     public void addWorldPath(final String searchPath) {
366         worldPaths.add(searchPath);
367 
368     }
369 
370     /**
371      * Returns an array of search paths for worlds.
372      *
373      * @return
374      *      array of search paths
375      */
376     public String[] getWorldPaths() {
377         ensureLoad();
378         String[] paths = new String[worldPaths.size()];
379         return (String[]) worldPaths.toArray(paths);
380     }
381 
382 
383     private void clearWorldLocators() {
384         worldLocators.clear();
385     }
386 
387 
388     /**
389      * Adds a world locator used to find worlds.
390      *
391      * @param locator
392      *      the locator to be added
393      */
394     public void addWorldLocator(final WorldLocator locator) {
395         worldLocators.add(locator);
396     }
397 
398     /**
399      * Returns an array of world locators.
400      *
401      * @return
402      *      an array of locators
403      */
404     public WorldLocator[] getWorldLocators() {
405         ensureLoad();
406         WorldLocator[] locators = new WorldLocator[worldLocators.size()];
407         return (WorldLocator[]) worldLocators.toArray(locators);
408     }
409 
410 
411     private String toWorldPath(final String prefix, final String suffix) {
412         File theFile = new File(prefix);
413 
414         if (!theFile.exists() && (servletContext != null)) {
415             String scPath = servletContext.getRealPath("/");
416             theFile = new File(scPath, prefix);
417             if (theFile.exists()) {
418                 return (new File(theFile, suffix)).getPath();
419             }
420         }
421 
422         String worldPath = prefix + (prefix.endsWith("/") ? "" : "/") + suffix;
423 
424         return worldPath;
425     }
426 
427 
428     /**
429      * Locates a world with the given name.
430      *
431      * @param worldName
432      *      the name of the world
433      * @return
434      *      a molwind world descriptor
435      * @throws java.io.IOException
436      *      is thrown upon i/o error
437      */
438     public MolwindWorld locateWorld(final String worldName)
439     throws IOException {
440         ensureLoad();
441         MolwindWorld world = null;
442         WorldLocator[] locators = getWorldLocators();
443         String[] worldPaths = getWorldPaths();
444 
445         for (int i = 0; i < locators.length; i++) {
446             for (int j = 0; j < worldPaths.length; j++) {
447                 world = locators[i].locateWorld(toWorldPath(worldPaths[j],
448                         worldName));
449                 if (world != null) {
450                     return world;
451                 }
452             }
453         }
454 
455         return null;
456     }
457 
458     /**
459      * Get the ServletContext value.
460      *
461      * @return
462      *      the ServletContext value
463      */
464     public ServletContext getServletContext() {
465         return servletContext;
466     }
467 
468     /**
469      * Set the ServletContext value.
470      *
471      * @param newServletContext
472      *      the new ServletContext value
473      */
474     public void setServletContext(final ServletContext newServletContext) {
475         this.servletContext = newServletContext;
476     }
477 
478 
479     private BigDecimal convertTimestamp(final String pVal) {
480         BigDecimal bDec = null;
481         try {
482             Timestamp timestamp = Timestamp.valueOf(pVal);
483             bDec = BigDecimal.valueOf(timestamp.getTime());
484         } catch (IllegalArgumentException iae) {
485             bDec = null;
486         }
487         return bDec;
488     }
489 
490     private BigDecimal numberInit(final String pVal) {
491         if (pVal.length() <= 0) {
492             return BigDecimal.ZERO;
493         }
494 
495         BigDecimal bDec = null;
496         try {
497             bDec = new BigDecimal(pVal);
498         } catch (NumberFormatException nfe) {
499             // Try special cases of numbers, such as timestamp, dates, times
500             bDec = convertTimestamp(pVal);
501         }
502         if (bDec == null) {
503             throw new NumberFormatException();
504         }
505         return bDec;
506     }
507 
508     private Object[] createParam(final Class pType, final String pVal) {
509         Object[] param = new Object[1];
510 
511         if (pType.equals(Boolean.class) || pType.equals(Boolean.TYPE)) {
512             param[0] = Boolean.valueOf(Stringx.toBoolean(pVal, false));
513         } else if (pType.equals(Character.class)
514                 || pType.equals(Character.TYPE)) {
515             param[0] = Character.valueOf(
516                     ((pVal.length() > 0) ? pVal.charAt(0) : (char) 0)
517             );
518         } else if (pType.equals(Byte.class) || pType.equals(Byte.TYPE)) {
519             if (pVal.matches("0[01]+") || pVal.matches("[01]{4,8}+")) {
520                 param[0] = Byte.valueOf(Byte.parseByte(pVal, 2));
521             } else {
522                 param[0] = Byte.valueOf(pVal);
523             }
524         } else if (pType.equals(Short.class) || pType.equals(Short.TYPE)) {
525             param[0] = Short.valueOf((numberInit(pVal)).shortValue());
526         } else if (pType.equals(Integer.class) || pType.equals(Integer.TYPE)) {
527             param[0] = Integer.valueOf((numberInit(pVal)).intValue());
528         } else if (pType.equals(Long.class) || pType.equals(Long.TYPE)) {
529             param[0] = Long.valueOf((numberInit(pVal)).longValue());
530         } else if (pType.equals(Float.class) || pType.equals(Float.TYPE)) {
531             param[0] = Float.valueOf((numberInit(pVal)).floatValue());
532         } else if (pType.equals(Double.class) || pType.equals(Double.TYPE)) {
533             param[0] = Double.valueOf((numberInit(pVal)).doubleValue());
534         } else if (pType.equals(String.class)) {
535             param[0] = pVal;
536         } else if (pType.equals(BigDecimal.class)) {
537             param[0] = numberInit(pVal);
538         } else {
539             // try first to interpret pVal as class name...
540             try {
541                 Class clazz = Class.forName(pVal);
542                 param[0] = clazz.newInstance();
543                 return param;
544             } catch (ClassNotFoundException cnfe) {
545                 cnfe.printStackTrace();
546             } catch (InstantiationException iex) {
547                 iex.printStackTrace();
548             } catch (IllegalAccessException iax) {
549                 iax.printStackTrace();
550             }
551 
552             // now try if the requested object can be instantiated using the
553             // given parameter value...
554             try {
555                 Constructor constructor = pType.getConstructor(pVal.getClass());
556                 param[0] = constructor.newInstance(pVal);
557             } catch (Exception ex) {
558                 ex.printStackTrace();
559                 param = null;
560             }
561         }
562 
563         return param;
564     }
565 
566     private void invokeSetter(final Object object, final Properties properties)
567     throws ConfigException {
568         Class clazz = object.getClass();
569         Method[] methods = clazz.getMethods();
570         Enumeration en = properties.propertyNames();
571 
572         while (en.hasMoreElements()) {
573             String an = (String) en.nextElement();
574             String cont = properties.getProperty(an);
575             String mName = "set" + an.toLowerCase();
576 
577             for (int i = 0; i < methods.length; i++) {
578                 if (methods[i].getName().equalsIgnoreCase(mName)) {
579                     Class[] pTypes = methods[i].getParameterTypes();
580                     if (pTypes.length != 1) {
581                         continue;
582                     }
583                     Object[] param = createParam(pTypes[0], cont);
584                     if (param == null) {
585                         continue;
586                     }
587                     try {
588                         methods[i].invoke(object, param);
589                     } catch (IllegalAccessException iae) {
590                         throw new ConfigException(iae.getMessage());
591                     } catch (InvocationTargetException itex) {
592                         throw new ConfigException(itex.getMessage());
593                     }
594                 }
595             }
596         }
597     }
598 
599 
600     /**
601      * Configures the given object.
602      *
603      * @param object
604      *      the object to be configured
605      * @throws org.molwind.util.ConfigException
606      *      is thrown upon config error
607      */
608     public void setup(final Configurable object)
609     throws ConfigException {
610         if (!configStack.isEmpty()) {
611             Properties properties = (Properties) configStack.pop();
612             invokeSetter(object, properties);
613         }
614 	
615     }
616 
617     /**
618      *
619      *@return 
620      *    returns an array of available worlds
621      *
622      */
623     public String[] getWorldNames() {
624 
625 	ArrayList<String> result = new ArrayList<String>();
626 	for(WorldLocator w:getWorldLocators()){
627 	    for(String s:w.getWorldNames()){
628 		result.add(s);
629 	    }
630 	    
631 	}
632       	String[] resultArray = new String[result.size()];
633 	int i = 0;
634 	for(String h:result){
635 	    resultArray[i]=h;
636 	    i++;
637 	}
638 
639 	return resultArray;
640 	
641 	
642     }
643 
644     
645 
646 
647 
648 
649 
650     /**
651      * Reads the config.txt file.
652      *
653      * @param path
654      *      path to the config file
655      */
656 //    public void initialise(String path){
657 //        confPath=path;
658 //        TextIO rt = new TextIO();
659 //        //System.out.println(confPath);
660 //        al=rt.readFile(confPath);
661 //
662 //        for (int i=0; i<al.size(); i++){
663 //            if (al.get(i).equals("<WorldCount>")){
664 //                worldCount=Integer.valueOf(al.get(i+1));
665 //            }
666 //            if (al.get(i).equals("<WorldNames>")){
667 //                for(int i1=0; i1<worldCount; i1++){
668 //                    //names.put(al.get(i+i1+1),i1);
669 //                    //names2.put(i1, al.get(i+i1+1));
670 //                    String time=String.valueOf(
671 //                            System.currentTimeMillis() / 1000);
672 //                    time=time.substring(time.length()-5, time.length()-1);
673 //                    time = time+String.valueOf(i1);
674 //                    System.out.println(i1);
675 //                    names.put(time,i1);
676 //                    names2.put(i1, time);
677 //                }
678 //            }
679 //            if (al.get(i).equals("<TargetFileURL>")){
680 //                for(int i1=0; i1<worldCount; i1++){
681 //                    url.add(al.get(i+i1+1));
682 //                }
683 //
684 //            }
685 //            if (al.get(i).equals("<inermediateLayers>")){
686 //                if (Integer.valueOf(al.get(i+1))==1){
687 //                    intermediateLayer=true;
688 //                }
689 //            }
690 //            if (al.get(i).equals("<ServerURL>")){
691 //                server=al.get(i+1);
692 //            }
693 //            if (al.get(i).equals("<ImagesXMLFileURL>")){
694 //                urlXML.add(al.get(i+1));
695 //            }
696 //            if (al.get(i).equals("<Features>")){
697 //                int more=1;
698 //                int i1=1;
699 //                while(more==1){
700 //                    String s=al.get(i+i1);
701 //                    if (s.equals("</Features>")){
702 //                        more=0;
703 //                    }
704 //                    else if(s.startsWith("//")){
705 //                        i1=i1-1;
706 //                    }
707 //                    else{
708 //                        features.add(s);
709 //                    }
710 //                    i1++;
711 //                }
712 //            }
713 //
714 //        }
715 //    }
716 
717     /**
718      * Re-reads the config file and updates the provided information.
719      */
720 //    public void update(){
721 //        TextIO rt = new TextIO();
722 //        al=rt.readFile(confPath);
723 //        int wcNew=worldCount;
724 //        for (int i=0; i<al.size(); i++){
725 //            if (al.get(i).equals("<WorldCount>")){
726 //                wcNew=Integer.valueOf(al.get(i+1));
727 //            }
728 //            if (al.get(i).equals("<WorldNames>")){
729 //                for(int i1=worldCount; i1<wcNew; i1++){
730 //                    //names.put(al.get(i+i1+1),i1);
731 //                    //names2.put(i1, al.get(i+i1+1));
732 //                    String time=String.valueOf(
733 //                            System.currentTimeMillis() / 1000);
734 //                    time=time.substring(time.length()-5, time.length()-1);
735 //                    time = time+String.valueOf(i1);
736 //                    System.out.println(i1);
737 //                    names.put(time,i1);
738 //                    names2.put(i1, time);
739 //                }
740 //            }
741 //        }
742 //        worldCount=wcNew;
743 //
744 //    }
745 
746     /**
747      * Returns the features which should be displayed
748      *
749      * @return
750      *      the features which should be displayed
751      */
752 //    public static ArrayList getFeatureList(){
753 //        return features;
754 //    }
755 
756     /**
757      * Returns the id of a dataset with a given name
758      *
759      * @param s
760      *      name of the dataset
761      * @return
762      *      the id of a dataset with a given name
763      */
764 //    public static int getWorldNumber(String s){
765 //        return names.get(s);
766 //    }
767 
768     /**
769      * returns the name of a dataset with a given id
770      *
771      * @param i
772      *      id of the dataset
773      * @return
774      *      name of the dataset
775      */
776 //    public static String getWorldName(int i){
777 //        return names2.get(i);
778 //    }
779 
780     /**
781      * returns the number of different datasets
782      *
783      * @return
784      *      number of different datasets
785      */
786 //    public static int getWorldCount(){
787 //        return worldCount;
788 //    }
789 
790     /**
791      * returns the path of the sd-file of a dataset-id
792      *
793      * @param i
794      *      id of a dataset
795      * @return
796      *      path of the sd-file of a dataset-id
797      */
798 //    public static String sdfLocation(int i){
799 //        return url.get(i);
800 //    }
801 
802     /**
803      * returns the zoomfactor of a dataset
804      *
805      * @param i
806      *      id of a dataset
807      * @return
808      *      returns the zoomfactor
809      */
810 //    public static double getZoom(int i) {
811 //        return zoom;
812 //    }
813 
814     /**
815      * returns the maximal number of molecules of level 0
816      *
817      * @param i
818      *      id of a dataset
819      * @return
820      *      maximal number of molecules of level 0
821      */
822 //    public static int getMolsPerLevel(int i){
823 //        return molsPerLevel;
824 //    }
825 
826     /**
827      * returns wheter intermadiate layers should be generated or not
828      *
829      * @param i
830      *      id of a dataset
831      * @return
832      *      true if intermediate layers should be generated, false if not.
833      */
834 //    public static boolean getIntermediateLayer(int i){
835 //        return intermediateLayer;
836 //    }
837 
838     /**
839      * returns the url of the server
840      *
841      * @return
842      *      url of the server
843      */
844 //    public static String getServerUrl(){
845 //        return server;
846 //    }
847 
848 //    public static void setStarted(int i){
849 //        isStarted.put(i, 1);
850 //    }
851 
852 //    public static int getStarted(int i){
853 //        if (isStarted.containsKey(i)){
854 //            return 1;
855 //        }
856 //        else{
857 //            return 0;
858 //        }
859 //    }
860 
861 }