1/* 2 * Copyright (c) 1997, 2015, 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 26package com.sun.codemodel.internal.fmt; 27 28import java.io.BufferedReader; 29import java.io.BufferedWriter; 30import java.io.IOException; 31import java.io.InputStream; 32import java.io.InputStreamReader; 33import java.io.OutputStream; 34import java.io.OutputStreamWriter; 35import java.io.PrintWriter; 36import java.text.ParseException; 37import java.util.Iterator; 38import java.util.List; 39 40import com.sun.codemodel.internal.JClass; 41import com.sun.codemodel.internal.JPackage; 42import com.sun.codemodel.internal.JResourceFile; 43import com.sun.codemodel.internal.JTypeVar; 44 45/** 46 * Statically generated Java soruce file. 47 * 48 * <p> 49 * This {@link JResourceFile} implementation will generate a Java source 50 * file by copying the source code from a resource. 51 * <p> 52 * While copying a resource, we look for a package declaration and 53 * replace it with the target package name. This allows the static Java 54 * source code to have an arbitrary package declaration. 55 * <p> 56 * You can also use the getJClass method to obtain a {@link JClass} 57 * object that represents the static file. This allows the client code 58 * to refer to the class from other CodeModel generated code. 59 * <p> 60 * Note that because we don't parse the static Java source code, 61 * the returned {@link JClass} object doesn't respond to methods like 62 * "isInterface" or "_extends", 63 * 64 * @author 65 * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) 66 */ 67public final class JStaticJavaFile extends JResourceFile { 68 69 private final JPackage pkg; 70 private final String className; 71 private final ResourceLoader source; 72 private final JStaticClass clazz; 73 private final LineFilter filter; 74 75 public JStaticJavaFile(JPackage _pkg, String _className, Class<?> loadingClass, LineFilter _filter) { 76 super(_className + ".java"); 77 if (loadingClass == null) throw new NullPointerException(); 78 this.pkg = _pkg; 79 this.clazz = new JStaticClass(); 80 this.className = _className; 81 this.source = new ResourceLoader(_className, loadingClass); 82 this.filter = _filter; 83 } 84 85 /** 86 * Returns a class object that represents a statically generated code. 87 */ 88 public final JClass getJClass() { 89 return clazz; 90 } 91 92 protected boolean isResource() { 93 return false; 94 } 95 96 protected void build(OutputStream os) throws IOException { 97 int lineNumber=1; 98 try ( 99 InputStream is = source.getResourceAsStream(); 100 BufferedReader r = new BufferedReader(new InputStreamReader(is)); 101 PrintWriter w = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os))); 102 ) { 103 LineFilter filter = createLineFilter(); 104 String line; 105 while((line=r.readLine())!=null) { 106 line = filter.process(line); 107 if(line!=null) 108 w.println(line); 109 lineNumber++; 110 } 111 } catch( ParseException e ) { 112 throw new IOException("unable to process "+source+" line:"+lineNumber+"\n"+e.getMessage()); 113 } 114 } 115 116 /** 117 * Creates a {@link LineFilter}. 118 * <p> 119 * A derived class can override this method to process 120 * the contents of the source file. 121 */ 122 private LineFilter createLineFilter() { 123 // this filter replaces the package declaration. 124 LineFilter f = new LineFilter() { 125 public String process(String line) { 126 if(!line.startsWith("package ")) return line; 127 128 // replace package decl 129 if( pkg.isUnnamed() ) 130 return null; 131 else 132 return "package "+pkg.name()+";"; 133 } 134 }; 135 if( filter!=null ) 136 return new ChainFilter(filter,f); 137 else 138 return f; 139 } 140 141 /** 142 * Filter that alters the Java source code. 143 * <p> 144 * By implementing this interface, derived classes 145 * can modify the Java source file before it's written out. 146 */ 147 public interface LineFilter { 148 /** 149 * @param line 150 * a non-null valid String that corresponds to one line. 151 * No '\n' included. 152 * @return 153 * null to strip the line off. Otherwise the returned 154 * String will be written out. Do not add '\n' at the end 155 * of this string. 156 * 157 * @exception ParseException 158 * when for some reason there's an error in the line. 159 */ 160 String process(String line) throws ParseException; 161 } 162 163 /** 164 * A {@link LineFilter} that combines two {@link LineFilter}s. 165 */ 166 public final static class ChainFilter implements LineFilter { 167 private final LineFilter first,second; 168 public ChainFilter( LineFilter first, LineFilter second ) { 169 this.first=first; 170 this.second=second; 171 } 172 public String process(String line) throws ParseException { 173 line = first.process(line); 174 if(line==null) return null; 175 return second.process(line); 176 } 177 } 178 179 180 private class JStaticClass extends JClass { 181 182 private final JTypeVar[] typeParams; 183 184 JStaticClass() { 185 super(pkg.owner()); 186 // TODO: allow those to be specified 187 typeParams = new JTypeVar[0]; 188 } 189 190 public String name() { 191 return className; 192 } 193 194 public String fullName() { 195 if(pkg.isUnnamed()) 196 return className; 197 else 198 return pkg.name()+'.'+className; 199 } 200 201 public JPackage _package() { 202 return pkg; 203 } 204 205 public JClass _extends() { 206 throw new UnsupportedOperationException(); 207 } 208 209 public Iterator<JClass> _implements() { 210 throw new UnsupportedOperationException(); 211 } 212 213 public boolean isInterface() { 214 throw new UnsupportedOperationException(); 215 } 216 217 public boolean isAbstract() { 218 throw new UnsupportedOperationException(); 219 } 220 221 public JTypeVar[] typeParams() { 222 return typeParams; 223 } 224 225 protected JClass substituteParams(JTypeVar[] variables, List<JClass> bindings) { 226 return this; 227 } 228 } 229 230 static class ResourceLoader { 231 Class<?> loadingClass; 232 String shortName; 233 234 ResourceLoader(String shortName, Class<?> loadingClass) { 235 this.loadingClass = loadingClass; 236 this.shortName = shortName; 237 } 238 239 InputStream getResourceAsStream() { 240 // some people didn't like our jars to contain files with .java extension, 241 // so when we build jars, we'' use ".java_". But when we run from the workspace, 242 // we want the original source code to be used, so we check both here. 243 // see bug 6211503. 244 InputStream stream = loadingClass.getResourceAsStream(shortName + ".java"); 245 if (stream == null) { 246 stream = loadingClass.getResourceAsStream(shortName + ".java_"); 247 } 248 if (stream == null) { 249 throw new InternalError("Unable to load source code of " + loadingClass.getName() + " as a resource"); 250 } 251 return stream; 252 } 253 254 } 255 256} 257