001/* Build documentation for Java and Actors
002
003 Copyright (c) 2006-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.vergil.actor;
029
030import java.io.File;
031import java.util.Iterator;
032import java.util.LinkedList;
033import java.util.List;
034
035import ptolemy.actor.gui.Configuration;
036import ptolemy.data.BooleanToken;
037import ptolemy.data.expr.Parameter;
038import ptolemy.data.type.BaseType;
039import ptolemy.kernel.util.Attribute;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.KernelException;
042import ptolemy.kernel.util.NameDuplicationException;
043import ptolemy.kernel.util.NamedObj;
044import ptolemy.kernel.util.StringAttribute;
045import ptolemy.util.ExecuteCommands;
046import ptolemy.util.FileUtilities;
047import ptolemy.util.StreamExec;
048import ptolemy.util.StringUtilities;
049
050///////////////////////////////////////////////////////////////////
051//// DocBuilder
052
053/** Build Documentation for Java and Actors.
054 *
055 *  <p>This class sets the commands that build the Java classes.
056 *
057 *  @author Christopher Brooks
058 *  @version $Id$
059 *  @since Ptolemy II 5.2
060 *  @Pt.ProposedRating Yellow (eal)
061 *  @Pt.AcceptedRating Yellow (eal)
062 */
063public class DocBuilder extends Attribute {
064
065    // In principle, this class should be usable from both within a UI
066    // and without a UI.
067
068    /** Create a new instance of the DocBuilder.
069     *  @param container The container.
070     *  @param name The name of the code generator.
071     *  @exception IllegalActionException If the super class throws the
072     *   exception or error occurs when setting the file path.
073     *  @exception NameDuplicationException If the super class throws the
074     *   exception or an error occurs when setting the file path.
075     */
076    public DocBuilder(NamedObj container, String name)
077            throws IllegalActionException, NameDuplicationException {
078        super(container, name);
079        cleanFirst = new Parameter(this, "cleanFirst");
080        cleanFirst.setTypeEquals(BaseType.BOOLEAN);
081
082        cleanFirst.setExpression("true");
083    }
084
085    ///////////////////////////////////////////////////////////////////
086    ////                     parameters                            ////
087
088    /** If true, then clean before building documentation.  The default
089     *  value is true because if a user is adding an actor, then the
090     *  codeDoc/tree.html, codeDoc/ptolemy/actor/lib/Ramp.xml,
091     *  and codeDoc/ptolemy/actor/lib/RampIdx.xml files might already
092     *  exist.  It is safer to force a clean each time because the
093     *  makefile does not accurately capture the dependencies between
094     *  .java sources and .html, .xml and Idx.xml files.
095     */
096    public Parameter cleanFirst;
097
098    ///////////////////////////////////////////////////////////////////
099    ////                         public methods                    ////
100
101    /** Build the Java class and Actor documentation.
102     *  The default is to run make in <code>$PTII/doc</code>.
103     *
104     *  However, if the configuration set by {@link
105     *  #setConfiguration(Configuration)} then the configuration is
106     *  searched for a _docApplicationSpecializer parameter.  If that
107     *  parameter exists it is assumed to name a class that implements
108     *  the {@link DocApplicationSpecializer} interface and the
109     *  {@link DocApplicationSpecializer#buildCommands(ExecuteCommands)}
110     *  method which returns the commands to invoke.
111     *
112     *  @return The return value of the last subprocess that was executed.
113     *  or -1 if no commands were executed.
114     *  @exception IllegalActionException If there is a problem building
115     *  the documentation.
116     */
117    public int buildDocs() throws IllegalActionException {
118        if (_executeCommands == null) {
119            _executeCommands = new StreamExec();
120        }
121        return _executeCommands();
122    }
123
124    /** Get the command executor, which can be either non-graphical
125     *  or graphical.  The initial default is non-graphical, which
126     *  means that stderr and stdout from subcommands is written
127     *  to the console.
128     *  @return executeCommands The subprocess command executor.
129     *  @see #setExecuteCommands(ExecuteCommands)
130     */
131    public ExecuteCommands getExecuteCommands() {
132        return _executeCommands;
133    }
134
135    /** Set the configuration.
136     *  @param configuration The configuration in which we look up the
137     *  _applicationName and _docApplicationSpecializer parameters.
138     */
139    public void setConfiguration(Configuration configuration) {
140        _configuration = configuration;
141    }
142
143    /** Set the command executor, which can be either non-graphical
144     *  or graphical.  The initial default is non-graphical, which
145     *  means that stderr and stdout from subcommands is written
146     *  to the console.
147     *  @param executeCommands The subprocess command executor.
148     *  @see #getExecuteCommands()
149     */
150    public void setExecuteCommands(ExecuteCommands executeCommands) {
151        _executeCommands = executeCommands;
152    }
153
154    ///////////////////////////////////////////////////////////////////
155    ////                         private methods                   ////
156
157    /** Return the command to compile ptII/doc/doclets/PtDoclet.
158     */
159    private static String _compilePtDoclet(File ptII) {
160        String results = "";
161        try {
162            String javaHome = StringUtilities.getProperty("java.home");
163            if (javaHome != null && javaHome.length() > 1) {
164                javaHome = javaHome.replace('\\', '/');
165                String toolsJarFileBase = "/../lib/tools.jar";
166                File toolsJarFile = new File(javaHome + toolsJarFileBase);
167                if (toolsJarFile.exists()) {
168                    results = "javac -classpath \"" + ptII + File.pathSeparator
169                            + javaHome + toolsJarFileBase
170                            + "\" doc/doclets/PtDoclet.java";
171                } else {
172                    if (StringUtilities.getProperty("os.name")
173                            .equals("Mac OS X")) {
174                        results = "javac -classpath \"" + ptII
175                                + "\" doc/doclets/PtDoclet.java";
176                    } else {
177                        results = "echo \"Warning: Failed to generate commands to compile "
178                                + "ptII/doc/doclets/PtDoclet.java. The jar file tools.jar at "
179                                + toolsJarFile.getCanonicalPath()
180                                + " does not exist?\"";
181                    }
182                }
183            }
184        } catch (Throwable throwable) {
185            results = "echo \"Warning, failed to generate command "
186                    + "to compile ptII/doc/doclets/PtDoclet.java: "
187                    + KernelException.stackTraceToString(throwable) + "\"";
188
189        }
190        return results;
191    }
192
193    /** Build the documentation.
194     *  @return The return value of the last subprocess that was executed
195     *  or -1 if no commands were executed.
196     */
197    private int _executeCommands() throws IllegalActionException {
198
199        File ptII = new File(
200                StringUtilities.getProperty("ptolemy.ptII.dir") + "/doc");
201        _executeCommands.setWorkingDirectory(ptII);
202
203        List commands = null;
204        // Search for a _docApplicationSpecializer in the configuration.
205        Parameter docApplicationSpecializerParameter = null;
206        if (_configuration != null) {
207            docApplicationSpecializerParameter = (Parameter) _configuration
208                    .getAttribute("_docApplicationSpecializer",
209                            Parameter.class);
210        }
211        if (docApplicationSpecializerParameter != null) {
212            String docApplicationSpecializerClassName = docApplicationSpecializerParameter
213                    .getExpression();
214
215            try {
216                Class docApplicationSpecializerClass = Class
217                        .forName(docApplicationSpecializerClassName);
218                DocApplicationSpecializer docApplicationSpecializer = (DocApplicationSpecializer) docApplicationSpecializerClass
219                        .newInstance();
220                commands = docApplicationSpecializer
221                        .buildCommands(_executeCommands);
222            } catch (Throwable throwable) {
223                throw new IllegalActionException(
224                        "Failed to call doc application initializer "
225                                + "class \""
226                                + docApplicationSpecializerClassName
227                                + "\" buildCommands() method");
228            }
229        } else {
230            commands = new LinkedList();
231            String applicationName = null;
232
233            try {
234                StringAttribute applicationNameAttribute = (StringAttribute) _configuration
235                        .getAttribute("_applicationName",
236                                StringAttribute.class);
237
238                if (applicationNameAttribute != null) {
239                    applicationName = applicationNameAttribute.getExpression();
240                }
241            } catch (Throwable throwable) {
242                // Ignore and use the default applicationName: "",
243                // which means we look in doc.codeDoc.
244            }
245
246            // Windows users might not have the rm command.
247            if (((BooleanToken) cleanFirst.getToken()).booleanValue()) {
248                String codeDocDirectory = ptII + "/codeDoc";
249                _executeCommands.updateStatusBar("Deleting the contents of \""
250                        + codeDocDirectory + "\".");
251                if (!FileUtilities.deleteDirectory(codeDocDirectory)) {
252                    _executeCommands.stderr(
253                            "Warning: Could not delete some of the files in \""
254                                    + codeDocDirectory + "\".");
255                }
256            }
257
258            if (applicationName == null) {
259                File ptIImk = new File(
260                        StringUtilities.getProperty("ptolemy.ptII.dir")
261                                + "/mk/ptII.mk");
262                // If the user has run configure, then we run make,
263                // otherwise we run the javadoc command.
264                if (ptIImk.exists()) {
265                    commands.add("make codeDoc/js/index.html");
266                    commands.add("make codeDoc/tree.html");
267                    commands.add("make codeDoc/ptolemy/actor/lib/Ramp.xml");
268                    commands.add("make codeDoc/ptolemy/actor/lib/RampIdx.xml");
269                } else {
270                    ptII = new File(
271                            StringUtilities.getProperty("ptolemy.ptII.dir"));
272                    _executeCommands.setWorkingDirectory(ptII);
273                    _executeCommands.updateStatusBar(
274                            "When creating docs, warnings are ok.");
275
276                    commands.add(_compilePtDoclet(ptII));
277
278                    if (FileUtilities.inPath("ant")) {
279                        commands.add("ant jsdoc ptdoc");
280                    } else {
281                        String message = "ant not found? jsdoc and ptdoc will not be run.";
282                        _executeCommands.updateStatusBar(message);
283                        System.err.println(message);
284                    }
285
286                    String styleSheetFile = "";
287                    //Unfortunately, -stylesheetfile is not supported with -doclet?
288                    //                     String styleSheetFileName = ptII + "/doc/doclets/stylesheet.css";
289                    //                     if (new File(styleSheetFileName).exists()) {
290                    //                         styleSheetFile = "-stylesheetfile " + styleSheetFileName + " ";
291                    //                     } else {
292                    //                         System.out.println("DocBuilder: Warning: could not find "
293                    //                                 + styleSheetFileName + ". The JavaDoc output might "
294                    //                                 + "be hard to read from within Vergil.");
295                    //                     }
296
297                    //                    commands.add("which javadoc");
298                    commands.add("javadoc -classpath \""
299                            + StringUtilities.getProperty("java.class.path")
300                            + "\" -J-Xmx512m -d doc/codeDoc " + styleSheetFile
301                            + "-doclet doc.doclets.PtDoclet "
302                            + "-subpackages com:diva:jni:org:ptolemy:thales "
303                            + "-exclude ptolemy.apps:ptolemy.copernicus:diva.util.java2d.svg");
304                    commands.add("java -Xmx256m "
305                            // ptolemy.ptII.batchMode is checked in MessageHandler.
306                            // Avoid hanging yes/no questions and just automatically
307                            // cancel because when this command is run the user
308                            // has no way to hit enter.
309                            + "-Dptolemy.ptII.batchMode=true " + "-classpath \""
310                            + StringUtilities.getProperty("java.class.path")
311                            + "\" ptolemy.moml.filter.ActorIndex doc/codeDoc/allNamedObjs.txt "
312                            + "\"" + ptII
313                            + "/ptolemy/configs/doc/models.txt\" doc/codeDoc");
314                }
315            } else {
316                commands.add("make codeDoc/js/index.html");
317                commands.add("make codeDoc" + applicationName
318                        + "/doc/codeDoc/tree.html");
319                commands.add("make APPLICATION=" + applicationName
320                        + " \"PTDOCFLAGS=-d doc/codeDoc" + applicationName
321                        + "/doc/codeDoc" + " codeDoc" + applicationName
322                        + "/ptolemy/actor/lib/Ramp.xml");
323                commands.add("make APPLICATION=" + applicationName + " codeDoc"
324                        + applicationName + "/ptolemy/actor/lib/RampIdx.xml");
325            }
326            if (commands.size() == 0) {
327                return -1;
328            }
329        }
330
331        _executeCommands.setCommands(commands);
332
333        try {
334            // FIXME: need to put this output in to the UI, if any.
335            _executeCommands.start();
336        } catch (Exception ex) {
337            StringBuffer errorMessage = new StringBuffer();
338            Iterator allCommands = commands.iterator();
339            while (allCommands.hasNext()) {
340                errorMessage.append((String) allCommands.next() + "\n");
341            }
342            throw new IllegalActionException(
343                    "Problem executing the " + "commands:\n" + errorMessage);
344        }
345        return _executeCommands.getLastSubprocessReturnCode();
346    }
347
348    ///////////////////////////////////////////////////////////////////
349    ////                         private variables                 ////
350
351    /** The configuration in which we look up the
352     *  _applicationName and _docApplicationSpecializer parameters.
353     */
354    private Configuration _configuration;
355
356    /** The object that actually executes the commands.
357     */
358    private ExecuteCommands _executeCommands;
359}