001 
002 /*
003  *  Descripter 1.0 - Java Script Engines
004  *  Copyright (C) 2010-2015  Jianjun Liu (J.J.Liu)
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package org.descripter.js;
021 
022 import java.io.ByteArrayOutputStream;
023 import java.io.OutputStream;
024 import java.net.URI;
025 import java.util.Arrays;
026 import java.util.HashMap;
027 import java.util.Map;
028 
029 import javax.tools.FileObject;
030 import javax.tools.ForwardingJavaFileManager;
031 import javax.tools.JavaFileManager;
032 import javax.tools.JavaFileObject;
033 import javax.tools.SimpleJavaFileObject;
034 import javax.tools.ToolProvider;
035 import javax.tools.JavaFileObject.Kind;
036 
037 /**
038  * <p>Represents a memory class loader to compile Java source code on the fly.</p>
039  * 
040  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.descripter.org" target="_blank">http://www.descripter.org</a>
041  * @since Descripter 1.0
042  */
043 public class Memory extends ClassLoader
044 {
045     private final Map<String, ByteArrayOutputStream> map = new HashMap<String, ByteArrayOutputStream>();
046     private final boolean verbose;
047 
048     /**
049      * <p>Constructs a class loader of this type.</p>
050      * @param verbose Compiles in verbose mode if it is set <tt>true</tt>; silently, otherwise.
051      * @since Descripter 1.0
052      */
053     public Memory(boolean verbose) {
054         this.verbose = verbose;
055     }
056 
057     /**
058      * <p>Constructs a class loader of this type.</p>
059      * @since Descripter 1.0
060      */
061     public Memory() {
062         this(false);
063     }
064 
065     /**
066      * <p>Finds the class with the name specified.</p>
067      * @param name The name of the class to find.
068      * @return The found class.
069      * @throws ClassNotFoundException if the specified class is not found.
070      * @since Descripter 1.0
071      */
072     @Override
073     protected synchronized Class<?> findClass(String name) throws ClassNotFoundException {
074         ByteArrayOutputStream baos = map.remove(name);
075         if (baos != null) {
076             byte[] ba = baos.toByteArray();
077             return defineClass(name, ba, 0, ba.length);
078         }
079         return super.findClass(name);
080     }
081 
082     /**
083      * <p>Compiles the specified Java source.</p>
084      * @param name The name of the target class.
085      * @param code The code of the Java source.
086      * @return <tt>true</tt> if the compilation is successful; <tt>false</tt>, otherwise.
087      * @since Descripter 1.0
088      */
089     public Boolean compile(final String name, final String code) {
090         return ToolProvider.getSystemJavaCompiler().getTask(
091                 null,
092                 new ForwardingJavaFileManager<JavaFileManager>(
093                         ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null)
094                 ) {
095                     @Override
096                     public JavaFileObject getJavaFileForOutput(
097                             Location location, final String name, Kind kind, FileObject source
098                     ) {
099                         return new SimpleJavaFileObject(URI.create(
100                                 "memo:///" + name.replace('.', '/') + kind.extension), kind) {
101                             @Override
102                             public OutputStream openOutputStream() {
103                                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
104                                 map.put(name, baos);
105                                 return baos;
106                             }
107                         };
108                     }
109                 },
110                 null,
111                 verbose ? Arrays.asList("-verbose") : null,
112                 null,
113                 Arrays.asList(new SimpleJavaFileObject(
114                         URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension),
115                         Kind.SOURCE
116                 ) {
117                     @Override
118                     public CharSequence getCharContent(boolean ignoreEncodingErrors) {
119                         return code;
120                     }
121                 })
122         ).call();
123     }
124 }