/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common * Development and Distribution License("CDDL") (collectively, the * "License"). You may not use this file except in compliance with the * License. You can obtain a copy of the License at * http://www.netbeans.org/cddl-gplv2.html * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the * specific language governing permissions and limitations under the * License. When distributing the software, include this License Header * Notice in each file and include the License file at * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the * License Header, with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * Contributor(s): * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. * * If you wish your version of this file to be governed by only the CDDL * or only the GPL Version 2, indicate your decision by adding * "[Contributor] elects to include this software in this distribution * under the [CDDL or GPL Version 2] license." If you do not indicate a * single choice of license, a recipient has the option to distribute * your version of this file under either the CDDL, the GPL Version 2 or * to extend the choice of license to its licensees as provided above. * However, if you add GPL Version 2 code and therefore, elected the GPL * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. */ package generator; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; import java.util.Comparator; import java.util.logging.Level; import java.util.logging.Logger; /** * @todo This should be rewritten as a small Ruby program and folded into the rdocscanner project * @todo Pull the Ruby versions etc. out from the actual source * @todo Improve arglist reduction in rdocscanner * @todo Deal with invalid constant RHS contents? * @todo Handle the "Document-method" etc. classes/comments? * @todo Try to prune out "empty" definitions like just "module Kernel\nend" etc. which I see * in some places, such as ext/bigdecimal. * @todo Suggestion from Mike McKinney: * one suggestion would be to have the actual location of the C files those stubs were build from if possible... i.e.: * OpenSSL::Cipher::Cipher could have ext/openssl/ossl_ssl.c in a comment atop the file or something along those lines. * * @author Tor Norbye */ public class Main { /** Creates a new instance of Main */ public Main(String[] args) throws IOException { if ((args.length != 2) && (args.length != 3)) { System.err.println("Usage: " + this.getClass().getName() + " <output> <rdocdata> <ext-rdocdata> \n" + "Processes special RDoc output data and builds up a stub tree\n" + "for NetBeans. The RDoc data is produced by the RubyStubsGenerator\n" + "modification for rdoc. The ext-rdoc data represents the ext/ directory in C Ruby's sourcebase\n" + "which if present will generate separate class additions rather than getting folded into the\n" + "normal class definition.\n"); System.exit(0); } File output = new File(args[0]); if (output.exists()) { System.err.println(output.getAbsolutePath() + " already exists"); System.exit(0); } File rdoc = new File(args[1]); if (!rdoc.exists()) { System.err.println(rdoc.getAbsolutePath() + " doesn't exist"); System.exit(0); } File extrdoc = null; if (args.length > 2) { extrdoc = new File(args[2]); if (!rdoc.exists()) { System.err.println(extrdoc.getAbsolutePath() + " doesn't exist"); System.exit(0); } } output.mkdirs(); File[] files = rdoc.listFiles(); sortFilesByName(files); for (File f : files) { if (f.isDirectory()) { processDir(f, null, output, null, null, false); } } if (extrdoc != null) { files = extrdoc.listFiles(); sortFilesByName(files); for (File f : files) { if (f.isDirectory()) { processDir(f, null, output, f.getName(), null, true); } } } } private void sortFilesByName(File[] files) { if (files != null) { Arrays.sort(files,new Comparator<File>() { public int compare(File o1, File o2) { return o1.getName().compareTo(o2.getName()); } }); } } private boolean processDir(File f, BufferedWriter bw, File output, String relativeName, String module, boolean isExt) throws IOException { boolean wroteSomething = false; System.out.println("Processing " + f.getPath()); assert f.isDirectory(); // This dir either corresponds to a class, or a module, or something else File[] children = f.listFiles(); sortFilesByName(children); if (children == null) { System.err.println("WARNING: No contents in " + f.getPath()); return false; } // Find classes String name; if (!isExt) { name = "stub_" + f.getName().toLowerCase() + ".rb"; } else { assert relativeName != null; name = relativeName + ".rb"; } boolean opened = false; boolean skip = false; for (File c : children) { if (c.getName().startsWith("cdesc-")) { // NOI18N if (Character.isLowerCase(f.getName().charAt(0))) { skip = true; // Skip classes like "fatal" - we can't generate valid // Ruby for these!! } else { // Found the class. Create a file. if (bw == null) { bw = new BufferedWriter(new FileWriter(new File(output, name))); addFileHeader(bw); opened = true; } addContents(bw, c); wroteSomething = true; } } } if (!skip && (bw != null)) { wroteSomething = true; if (module != null) { bw.write("module "); // NOI18N bw.write(module); bw.write("\n"); } // Add all the methods to the class for (File c : children) { if (c.isFile() && !c.getName().startsWith("cdesc-")) { // NOI18N addContents(bw, c); } } String m = null; // I shouldn't include module redefinitions in nested content! // if (module != null) { // m = module + "::" + f.getName(); // } else { // m = f.getName(); // } // Process nested classes for (File c : children) { if (c.isDirectory()) { String rel = relativeName; if (Character.isLowerCase(c.getName().charAt(0))) { if (relativeName != null) { rel = relativeName + File.separator + c.getName(); } else { rel = c.getName(); } } processDir(c, bw, output, rel, m, isExt); } } // The files don't contain the closing end statement // (such that it would be easy to add in the methods) bw.write("\nend\n"); // NOI18N if (module != null) { bw.write("\nend\n"); // NOI18N } } else if (!skip) { boolean added = false; if (isExt) { bw = new BufferedWriter(new FileWriter(new File(output, name))); addFileHeader(bw); opened = true; } for (File c : children) { if (c.isDirectory()) { String rel = relativeName; if (rel == null) { rel = f.getName(); } if (Character.isLowerCase(c.getName().charAt(0))) { if (relativeName != null) { rel = relativeName + File.separator + c.getName(); } else { rel = c.getName(); } } boolean modified = processDir(c, bw, output, rel, module, isExt); if (modified) { added = true; } } } if (isExt && !added) { // bw is pretty much empty so nuke it bw.close(); new File(output, name).delete(); } } if (opened) { bw.close(); } return wroteSomething; } private void addFileHeader(BufferedWriter bw) throws IOException { bw.write("# This is a machine-generated stub file for the NetBeans IDE Ruby Support.\n" + "#\n" + "# Many Ruby methods are \"built in\" which means that there is no\n" + "# Ruby source code for them. It is convenient for the IDE however to\n" + "# have these files for indexing and documentation purposes, so the following\n" + "# class definition is generated from the native implementation as a skeleton\n" + "# or stub to index.\n" + "#\n" + "# Ruby Version: \"1.8.7-p72\"\n" + //"# Ruby Patch Level: 72\n" + //"# Ruby Release: 20061225\n" + "# Generator version: 1.0\n" + "#\n" + // Create an empty line and empty comment such that the file header // isn't interpreter as a class comment for files missing documentation "\n" + "#\n"); } private void addContents(BufferedWriter bw, File c) throws FileNotFoundException, IOException { BufferedReader br = new BufferedReader(new FileReader(c)); while (true) { String line = br.readLine(); if (line == null) { break; } bw.write(line); bw.write("\n"); } br.close(); } /** * @param args the command line arguments */ public static void main(String[] args) { try { new generator.Main(args); } catch (IOException ex) { Logger.getLogger("global").log(Level.SEVERE, null, ex); } } }