1 /*
   2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.codemodel.internal.fmt;
  27 
  28 import java.io.BufferedReader;
  29 import java.io.BufferedWriter;
  30 import java.io.IOException;
  31 import java.io.InputStream;
  32 import java.io.InputStreamReader;
  33 import java.io.OutputStream;
  34 import java.io.OutputStreamWriter;
  35 import java.io.PrintWriter;
  36 import java.net.URL;
  37 import java.text.ParseException;
  38 import java.util.Iterator;
  39 import java.util.List;
  40 
  41 import com.sun.codemodel.internal.JClass;
  42 import com.sun.codemodel.internal.JPackage;
  43 import com.sun.codemodel.internal.JResourceFile;
  44 import com.sun.codemodel.internal.JTypeVar;
  45 
  46 /**
  47  * Statically generated Java soruce file.
  48  *
  49  * <p>
  50  * This {@link JResourceFile} implementation will generate a Java source
  51  * file by copying the source code from a resource.
  52  * <p>
  53  * While copying a resource, we look for a package declaration and
  54  * replace it with the target package name. This allows the static Java
  55  * source code to have an arbitrary package declaration.
  56  * <p>
  57  * You can also use the getJClass method to obtain a {@link JClass}
  58  * object that represents the static file. This allows the client code
  59  * to refer to the class from other CodeModel generated code.
  60  * <p>
  61  * Note that because we don't parse the static Java source code,
  62  * the returned {@link JClass} object doesn't respond to methods like
  63  * "isInterface" or "_extends",
  64  *
  65  * @author
  66  *     Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
  67  */
  68 public final class JStaticJavaFile extends JResourceFile {
  69 
  70     private final JPackage pkg;
  71     private final String className;
  72     private final URL source;
  73     private final JStaticClass clazz;
  74     private final LineFilter filter;
  75 
  76     public JStaticJavaFile(JPackage _pkg, String className, String _resourceName) {
  77         this( _pkg, className,
  78             SecureLoader.getClassClassLoader(JStaticJavaFile.class).getResource(_resourceName), null );
  79     }
  80 
  81     public JStaticJavaFile(JPackage _pkg, String _className, URL _source, LineFilter _filter ) {
  82         super(_className+".java");
  83         if(_source==null)   throw new NullPointerException();
  84         this.pkg = _pkg;
  85         this.clazz = new JStaticClass();
  86         this.className = _className;
  87         this.source = _source;
  88         this.filter = _filter;
  89     }
  90 
  91     /**
  92      * Returns a class object that represents a statically generated code.
  93      */
  94     public final JClass getJClass() {
  95         return clazz;
  96     }
  97 
  98     protected boolean isResource() {
  99         return false;
 100     }
 101 
 102     protected  void build(OutputStream os) throws IOException {
 103         InputStream is = source.openStream();
 104 
 105         BufferedReader r = new BufferedReader(new InputStreamReader(is));
 106         PrintWriter w = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os)));
 107         LineFilter filter = createLineFilter();
 108         int lineNumber=1;
 109 
 110         try {
 111             String line;
 112             while((line=r.readLine())!=null) {
 113                 line = filter.process(line);
 114                 if(line!=null)
 115                     w.println(line);
 116                 lineNumber++;
 117             }
 118         } catch( ParseException e ) {
 119             throw new IOException("unable to process "+source+" line:"+lineNumber+"\n"+e.getMessage());
 120         }
 121 
 122         w.close();
 123         r.close();
 124     }
 125 
 126     /**
 127      * Creates a {@link LineFilter}.
 128      * <p>
 129      * A derived class can override this method to process
 130      * the contents of the source file.
 131      */
 132     private LineFilter createLineFilter() {
 133         // this filter replaces the package declaration.
 134         LineFilter f = new LineFilter() {
 135             public String process(String line) {
 136                 if(!line.startsWith("package ")) return line;
 137 
 138                 // replace package decl
 139                 if( pkg.isUnnamed() )
 140                     return null;
 141                 else
 142                     return "package "+pkg.name()+";";
 143             }
 144         };
 145         if( filter!=null )
 146             return new ChainFilter(filter,f);
 147         else
 148             return f;
 149     }
 150 
 151     /**
 152      * Filter that alters the Java source code.
 153      * <p>
 154      * By implementing this interface, derived classes
 155      * can modify the Java source file before it's written out.
 156      */
 157     public interface LineFilter {
 158         /**
 159          * @param line
 160          *      a non-null valid String that corresponds to one line.
 161          *      No '\n' included.
 162          * @return
 163          *      null to strip the line off. Otherwise the returned
 164          *      String will be written out. Do not add '\n' at the end
 165          *      of this string.
 166          *
 167          * @exception ParseException
 168          *      when for some reason there's an error in the line.
 169          */
 170         String process(String line) throws ParseException;
 171     }
 172 
 173     /**
 174      * A {@link LineFilter} that combines two {@link LineFilter}s.
 175      */
 176     public final static class ChainFilter implements LineFilter {
 177         private final LineFilter first,second;
 178         public ChainFilter( LineFilter first, LineFilter second ) {
 179             this.first=first;
 180             this.second=second;
 181         }
 182         public String process(String line) throws ParseException {
 183             line = first.process(line);
 184             if(line==null)  return null;
 185             return second.process(line);
 186         }
 187     }
 188 
 189 
 190     private class JStaticClass extends JClass {
 191 
 192         private final JTypeVar[] typeParams;
 193 
 194         JStaticClass() {
 195             super(pkg.owner());
 196             // TODO: allow those to be specified
 197             typeParams = new JTypeVar[0];
 198         }
 199 
 200         public String name() {
 201             return className;
 202         }
 203 
 204         public String fullName() {
 205             if(pkg.isUnnamed())
 206                 return className;
 207             else
 208                 return pkg.name()+'.'+className;
 209         }
 210 
 211         public JPackage _package() {
 212             return pkg;
 213         }
 214 
 215         public JClass _extends() {
 216             throw new UnsupportedOperationException();
 217         }
 218 
 219         public Iterator<JClass> _implements() {
 220             throw new UnsupportedOperationException();
 221         }
 222 
 223         public boolean isInterface() {
 224             throw new UnsupportedOperationException();
 225         }
 226 
 227         public boolean isAbstract() {
 228             throw new UnsupportedOperationException();
 229         }
 230 
 231         public JTypeVar[] typeParams() {
 232             return typeParams;
 233         }
 234 
 235         protected JClass substituteParams(JTypeVar[] variables, List<JClass> bindings) {
 236             return this;
 237         }
 238     };
 239 }