Package writers

Source Code of writers.BaseWriter

package writers;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Locale;
import java.util.TimeZone;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.ConstructorDoc;
import com.sun.javadoc.Doc;
import com.sun.javadoc.ExecutableMemberDoc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.ParamTag;
import com.sun.javadoc.Parameter;
import com.sun.javadoc.ProgramElementDoc;
import com.sun.javadoc.SeeTag;
import com.sun.javadoc.Tag;

public class BaseWriter {
  // Some utilities

  public final static String MODE_JAVASCRIPT = "js";

  public BaseWriter()
  {

  }

  protected static boolean needsWriting(ProgramElementDoc doc){
    if( (doc != null) && Shared.i().isWebref(doc) )
    {
      return hasXMLDocument( doc );
    }
    return false;
  }

  protected static BufferedWriter makeWriter(String anchor) throws IOException
  {
    return makeWriter(anchor, false);
  }

  protected static String getWriterPath( String anchor, Boolean isLocal )
  {
    if (!isLocal) {
      return Shared.i().getOutputDirectory() + "/" + anchor;
    } else
    {
      return Shared.i().getLocalOutputDirectory() + anchor;
    }
  }

  protected static BufferedWriter makeWriter(String anchor, Boolean isLocal) throws IOException {
    FileWriter fw = new FileWriter( getWriterPath( anchor, isLocal ) );

    return new BufferedWriter(fw);
  }

  protected static String getAnchor(ProgramElementDoc doc)
  {
    String ret = getAnchorFromName(getName(doc));

    if(doc.containingClass() != null && !Shared.i().isRootLevel(doc.containingClass()))
    {
      ret = doc.containingClass().name() + "_" + ret;
    }

    if(!Shared.i().isCore(doc)){
      //add package name to anchor
      String[] parts = doc.containingPackage().name().split("\\.");
      String pkg = parts[parts.length-1] + "/";
      ret = "libraries/" + pkg + ret;
    }

    return ret;
  }

  protected static String getLocalAnchor(ProgramElementDoc doc)
  {
    String ret = getAnchorFromName(getName(doc));
    if(doc.containingClass() != null){
      ret = doc.containingClass().name() + "_" + ret;
    }

    return ret;
  }

  protected static String getReturnTypes(MethodDoc doc)
  {
    String ret = nameInPDE(doc.returnType().toString());
    if(doc.containingClass() != null)
    {
      for(MethodDoc m : doc.containingClass().methods())
      {
        if( m.name().equals(doc.name()) && m.returnType() != doc.returnType() )
        {
          String name = getSimplifiedType( nameInPDE(m.returnType().toString()) );
          if( ! ret.contains( name ) )
          { // add return type name if it's not already included
            ret += ", " + name;
          }
        }
      }
    }

    // add "or" (split first to make sure we don't mess up the written description)
    ret = ret.replaceFirst( ",([^,]+)$", ", or$1" );
    if( ! ret.matches(".+,.+,.+") )
    {
      ret = ret.replace( ",", "" );
    }

    return ret;
  }

  protected static String getSimplifiedType( String str )
  {
    if( str.equals("long") ){ return "int"; }
    if( str.equals("double") ){ return "float"; }

    return str;
  }

  protected static String getName(Doc doc) { // handle
    String ret = doc.name();
    if(doc instanceof MethodDoc)
    {
      ret = ret.concat("()");
    } else if (doc.isField()){
      // add [] if needed
      FieldDoc d = (FieldDoc) doc;
      ret = ret.concat(d.type().dimension());
    }
    return ret;
  }

  protected static String getAnchorFromName(String name){
    // change functionName() to functionName_
    if( name.endsWith("()") ){
      name = name.replace("()", "_");
    }
    // change "(some thing)" to something
    if( name.contains("(") && name.contains(")") ){
      int start = name.lastIndexOf("(") + 1;
      int end = name.lastIndexOf(")");
      name = name.substring(start, end);
      name = name.replace(" ", "");
    }
    // change thing[] to thing
    if( name.contains("[]")){
      name = name.replaceAll("\\[\\]", "");
    }
    // change "some thing" to "some_thing.html"
    name = name.replace(" ", "_").concat(".html");
    return name;
  }

  static protected String getBasicDescriptionFromSource(ProgramElementDoc doc) {
    return getBasicDescriptionFromSource(longestText(doc));
  }

  static protected String getBriefDescriptionFromSource(ProgramElementDoc doc) {
    Tag[] sta = doc.tags("brief");
    if(sta.length > 0){
      return sta[0].text();
    }
    return getBasicDescriptionFromSource(doc);
  }

  static protected String longestText(ProgramElementDoc doc){
    if(Shared.i().isWebref(doc)){
      //override longest rule if the element has an @webref tag
      return doc.commentText();
    }

    String s = doc.commentText();
    if( doc.isMethod() ){
      for(ProgramElementDoc d : doc.containingClass().methods()){
        if(d.name().equals(doc.name() ) ){
          if(d.commentText().length() > s.length()){
            s = d.commentText();
          }
        }
      }
    } else if(doc.isField()){
      for(ProgramElementDoc d : doc.containingClass().fields()){
        if(d.name().equals(doc.name() ) ){
          if(d.commentText().length() > s.length()){
            s = d.commentText();
          }
        }
      }
    }
    return s;
  }

  static protected String getBasicDescriptionFromSource(String s){
    String[] sa = s.split("(?i)(<h\\d>Advanced:?</h\\d>)|(=advanced)");
    if (sa.length != 0)
      s = sa[0];
    return s;
  }

  static protected String getAdvancedDescriptionFromSource(ProgramElementDoc doc) {
    return getAdvancedDescriptionFromString(longestText(doc));
  }
  static private String getAdvancedDescriptionFromString(String s) {
    String[] sa = s.split("(?i)(<h\\d>Advanced:?</h\\d>)|(=advanced)");
    if (sa.length > 1)
      s = sa[1];
    return s;
  }



  //

  protected static String getXMLPath(ProgramElementDoc doc) {
    String path = Shared.i().getXMLDirectory();
    String name = doc.name();
    String suffix = ".xml";
    if(doc.containingClass() != null){
      if(Shared.i().isRootLevel(doc.containingClass())){
        //inside PApplet or other root-level class
        if(doc instanceof FieldDoc){
          //if there is a method of the same name, append _var
          for( Doc d : doc.containingClass().methods()){
            if(d.name().equals(doc.name())){
              suffix = "_var" + suffix;
              break//don't append multiple times
            }
          }
        }
      } else {
        name = doc.containingClass().name() + "_" + name;
      }
    }

    if( !Shared.i().isCore(doc)){
      //if documentation is for a library element
      String[] pkg = doc.containingPackage().name().split("\\.");
      path = path + "LIB_" + pkg[pkg.length-1] + "/";
    }


    return path + name + suffix;
  }

  static protected String getExamples(ProgramElementDoc doc) throws IOException{
    return getExamples(getXMLPath(doc));
  }

  static private Document getXMLDocument(String path) throws IOException {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true);
    DocumentBuilder builder = null;
    Document doc = null;
    try {
      builder = factory.newDocumentBuilder();
    } catch (ParserConfigurationException e) {
      System.out.println( "Failed to load XML: " + e.getMessage());
    }

    if( builder != null )
    {
      try {
        doc = builder.parse(path);
      } catch (SAXException e) {
        System.out.println( "Failed to parse XML: " + e.getMessage() );
      } catch (IOException e) {
        System.out.println( "Failed to parse XML: " + e.getMessage() );
      }
    }


    return doc;
  }

  private static boolean hasXMLDocument( ProgramElementDoc doc )
  {
    String path = getXMLPath( doc );
    File f = new File( path );
    if( f.exists() )
    {
      return true;
    }
    return false;
  }

  static protected String getExamples(String path) throws IOException {
    Document doc = getXMLDocument(path);
    if( doc != null )
      return getExamples(doc);
    else
    {
      System.out.println("Unable to get examples from " + path + "; returning an empty string.");
      return "";
    }
  }

  protected static String getExamples(Document doc) throws IOException{
    //Parse the examples from an XML document
    TemplateWriter templateWriter = new TemplateWriter();
    ArrayList<HashMap<String, String>> exampleList = new ArrayList<HashMap<String, String>>();
    XPathFactory xpathFactory = XPathFactory.newInstance();
    XPath xpath = xpathFactory.newXPath();
    try {
      XPathExpression expr = xpath.compile("//example");
      Object result = expr.evaluate(doc, XPathConstants.NODESET);
      NodeList examples = (NodeList) result;

      for (int i = 0; i < examples.getLength(); i++) {
        HashMap<String, String> example = new HashMap<String, String>();

        expr = xpath.compile("image");
        String img = (String) expr.evaluate(examples.item(i),
            XPathConstants.STRING);
        expr = xpath.compile("code");
        String code = (String) expr.evaluate(examples.item(i),
            XPathConstants.STRING);

        example.put("image", Shared.i().getImageDirectory()
            + img);
        if(img.equals(""))
        {  // if no image, replace with empty string
          example.put("image", "");
        }
        example.put("code", code);

        exampleList.add(example);
      }

    } catch (XPathExpressionException e) {
      e.printStackTrace();
    }

    String exampleInner = templateWriter.writeLoop("/example.partial.html", exampleList);
    HashMap<String, String> map = new HashMap<String, String>();
    map.put("examples", exampleInner);
    return templateWriter.writePartial("examples.partial.html", map);
  }

  protected static String getXMLDescription(ProgramElementDoc doc) throws IOException {
    Document xmlDoc = getXMLDocument(getXMLPath(doc));
    if( xmlDoc != null )
      return getXMLDescription(xmlDoc);
    else {
      System.out.println("Unable to get description from " + getXMLPath(doc) + "; returning an empty string.");
      return "";
    }
  }

  /**
   *  Based upon Shared.addDescriptionTag().
   *
   *  Hint: this loads and adds js_mode/description as well
   */
  protected static String getXMLDescription(Document doc)
  {
    XPathFactory xpathFactory = XPathFactory.newInstance();
    XPath xpath = xpathFactory.newXPath();

    TemplateWriter templateWriter = new TemplateWriter();

    String desc = "";

    for( String component : Shared.i().getDescriptionSets() )
    {
      try
      {
        XPathExpression expr = xpath.compile(component);
        String result = (String) expr.evaluate(doc, XPathConstants.STRING);
        HashMap<String, String> vars = getDefaultDescriptionVars();
        if ( component.indexOf("js_mode") != -1 ) {
          vars.put( "description title", "JavaScript<br>\nNotes" );
        }
        if ( !result.equals("") )
        {
          vars.put( "description text", result );
          result = templateWriter.writePartial( "description.partial.html", vars );
        }
        desc += result;
      }
      catch ( XPathExpressionException e)
      {
        System.out.println("Error getting description from xml with expression: //" + component);
        e.printStackTrace();
      }
    }

    return desc;
  }

  protected static HashMap<String, String> getDefaultDescriptionVars ()
  {
    HashMap<String, String> vars = new HashMap<String, String>();
    vars.put("description title", "Description");
    vars.put("description text", "");
    return vars;
  }

  protected static String getTimestamp() {
    Calendar now = Calendar.getInstance();
    Locale us = new Locale("en");

    return now.getDisplayName(Calendar.MONTH, Calendar.LONG, us)
        + " "
        + now.get(Calendar.DAY_OF_MONTH)
        + ", "
        + now.get(Calendar.YEAR)
        + " "
        + FileUtils.nf(now.get(Calendar.HOUR), 2)
        + ":"
        + FileUtils.nf(now.get(Calendar.MINUTE), 2)
        + ":"
        + FileUtils.nf(now.get(Calendar.SECOND), 2)
        + now.getDisplayName(Calendar.AM_PM, Calendar.SHORT, us)
            .toLowerCase()
        + " "
        + TimeZone.getDefault().getDisplayName(
            TimeZone.getDefault().inDaylightTime(now.getTime()),
            TimeZone.SHORT, us);
  }

  /*
   * Get all the syntax possibilities for a method
   */
  protected static ArrayList<HashMap<String, String>> getSyntax(MethodDoc doc, String instanceName) throws IOException
  {
    TemplateWriter templateWriter = new TemplateWriter();
    ArrayList<HashMap<String, String>> ret = new ArrayList<HashMap<String,String>>();

    for( MethodDoc methodDoc : doc.containingClass().methods() )
    {
      if(Shared.i().shouldOmit(methodDoc)){
        continue;
      }
      if( methodDoc.name().equals(doc.name() ))
      {
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("name", methodDoc.name());
        map.put("object", instanceName);

        ArrayList<HashMap<String, String>> parameters = new ArrayList<HashMap<String,String>>();
        for( Parameter p : methodDoc.parameters() )
        {
          HashMap<String, String> paramMap = new HashMap<String, String>();
          paramMap.put("parameter", p.name());
          parameters.add(paramMap);
        }
        String params = templateWriter.writeLoop("method.parameter.partial.html", parameters, ", ");

        map.put("parameters", params);
        if( ! ret.contains(map) )
        {
          //don't put in duplicate function syntax
          ret.add(map);
        }
      }
    }
    return ret;
  }

  private static String removePackage(String name)
  { // keep everything after the last dot
    if( name.contains(".") )
    { return name.substring( name.lastIndexOf(".") + 1 ); }
    return name;
  }

  private static String nameInPDE(String fullName)
  {
    if( fullName.contains("<") && fullName.endsWith(">") )
    {  // if this type uses Java generics
      String parts[] = fullName.split("<");
      String generic = removePackage( parts[0] );
      String specialization = removePackage( parts[1] );
      specialization = specialization.substring( 0, specialization.length() - 1 );
      return generic + "&lt;" + specialization + "&gt;";
    }
    return removePackage( fullName );
  }

  protected static String getUsage(ProgramElementDoc doc){
    Tag[] tags = doc.tags("usage");
    if(tags.length != 0){
      return tags[0].text();
    }
    tags = doc.containingClass().tags("usage");
    if(tags.length != 0){
      return tags[0].text();
    }
    // return empty string if no usage is found
    return "";
  }

  protected static String getInstanceName(ProgramElementDoc doc){
    Tag[] tags = doc.containingClass().tags("instanceName");
    if(tags.length != 0){
      return tags[0].text().split("\\s")[0];
    }
    return "";
  }

  protected static String getInstanceDescription(ProgramElementDoc doc){
    Tag[] tags = doc.containingClass().tags("instanceName");
    if(tags.length != 0){
      String s = tags[0].text();
      return s.substring(s.indexOf(" "));
    }
    return "";
  }

  protected static String getParameters(MethodDoc doc) throws IOException{
    //get parent
    ClassDoc cd = doc.containingClass();
    ArrayList<HashMap<String, String>> ret = new ArrayList<HashMap<String,String>>();

    if(!Shared.i().isRootLevel(cd)){
      //add the parent parameter if this isn't a function of PApplet
      HashMap<String, String> parent = new HashMap<String, String>();
      parent.put("name", getInstanceName(doc));
      parent.put("description", cd.name() + ": " + getInstanceDescription(doc));
      ret.add(parent);
    }

    //get parameters from this and all other declarations of method
    for( MethodDoc m : cd.methods() ){
      if(Shared.i().shouldOmit(m)){
        continue;
      }
      if(m.name().equals(doc.name())){
        ret.addAll(parseParameters(m));
      }
    }

    removeDuplicateParameters(ret);


    TemplateWriter templateWriter = new TemplateWriter();
    return templateWriter.writeLoop("parameter.partial.html", ret);
  }

  protected static String getParameters(ClassDoc doc) throws IOException{
    ArrayList<HashMap<String, String>> ret = new ArrayList<HashMap<String,String>>();
    for( ConstructorDoc m : doc.constructors() ){
      if(Shared.i().shouldOmit(m)){
        continue;
      }
      ret.addAll(parseParameters(m));
    }
    removeDuplicateParameters(ret);

    TemplateWriter templateWriter = new TemplateWriter();
    return templateWriter.writeLoop("parameter.partial.html", ret);
  }

  protected static void removeDuplicateParameters(ArrayList<HashMap<String, String>> ret){
    // combine duplicate parameter names with differing types
    for(HashMap<String, String> parameter : ret)
    {
      String desc = parameter.get("description");
      if(!desc.endsWith(": "))
      {
        // if the chosen description has actual text
        // e.g. float: something about the float
        for(HashMap<String, String> parameter2 : ret)
        {
          String desc2 = parameter2.get("description");

          if( desc2.endsWith(": ") && parameter2.get("name").equals( parameter.get("name") ) )
          {
            // freshen up our variable with the latest description
            desc = parameter.get("description");

            if( ! desc.contains( desc2.substring( 0, desc2.indexOf(": ") ) ) )
            {
              // if the similar item doesn't have actual text
              // e.g. Boolean:
              String newDescription = desc2.replace(":", ",").concat( desc );
              parameter.put("description", newDescription);
            }
          }
        }
      }
    }
    //remove parameters without descriptions
    for( int i=ret.size()-1; i >= 0; i-- )
    {
      if(ret.get(i).get("description").endsWith(": "))
      {
        ret.remove(i);
      }
    }

    // add "or" (split first to make sure we don't mess up the written description)
    for( HashMap<String, String> param : ret )
    {
      String desc = param.get("description");
      String start = desc.substring( 0, desc.indexOf(":")+1 ).replaceFirst( ",([^,]+:)", ", or$1" );
      String end = desc.substring( desc.indexOf(":")+1, desc.length() );

      param.put( "description", start.concat( end ) );
    }
  }

  protected static ArrayList<HashMap<String, String>> parseParameters(ExecutableMemberDoc doc){
    ArrayList<HashMap<String, String>> ret = new ArrayList<HashMap<String,String>>();
    for( Parameter param : doc.parameters()){
      String type = getSimplifiedType( nameInPDE(param.type().toString()) ).concat(": ");
      String name = param.name();
      String desc = "";

      for( ParamTag tag : doc.paramTags() ){
        if(tag.parameterName().equals(name)){
          desc = desc.concat( tag.parameterComment() );
        }
      }

      HashMap<String, String> map = new HashMap<String, String>();
      map.put("name", name);
      map.put("description", type + desc);
      ret.add(map);
    }
    return ret;
  }

  /**
   *  Modes should support all API, so if XML not explicitly states "not supported", then assume it does.
   */
  protected static boolean isModeSupported ( ProgramElementDoc doc, String mode ) {

    Document xmlDoc = null;
    try {
      String xmlPath = getXMLPath( doc );
      xmlDoc = getXMLDocument( xmlPath );
    } catch ( IOException ioe ) {
      ioe.printStackTrace();
      return true;
    }

    XPathFactory xpathFactory = XPathFactory.newInstance();
    XPath xpath = xpathFactory.newXPath();

    try {

      String umraw = xpath.evaluate("//unsupported_modes", xmlDoc, XPathConstants.STRING).toString();
      String[] ums = umraw.split(",");
      for ( String s : ums ) {
        if ( s.trim().toLowerCase().equals(mode) )
          return false;
      }

    } catch ( XPathExpressionException e ) {

      e.printStackTrace();
    }

    return true;
  }

  protected static ArrayList<SeeTag> getAllSeeTags( ProgramElementDoc doc )
  {
    ArrayList<SeeTag> ret = new ArrayList<SeeTag>();
    ClassDoc cd = doc.containingClass();
    if( cd != null && doc.isMethod() )
    {  // if there is a containing class, get @see tags for all
      // methods with the same name as this one
      // Fixes gh issue 293
      for( MethodDoc m : cd.methods() )
      {
        if(m.name().equals(doc.name()))
        {
          for( SeeTag tag : m.seeTags() )
          {
            ret.add( tag );
          }
        }
      }
    }
    else
    // if no containing class (e.g. doc is a class)
      // just grab the see tags in the class doc comment
      for( SeeTag tag : doc.seeTags() )
      {
        ret.add( tag );
      }
    }
    return ret;
  }

  protected static String getRelated( ProgramElementDoc doc ) throws IOException
  {
    TemplateWriter templateWriter = new TemplateWriter();
    ArrayList<HashMap<String, String>> vars = new ArrayList<HashMap<String,String>>();

    // keep track of class members so that links to methods in e.g. PGraphics
    // that are copied into PApplet are correctly linked.
    HashMap<String, ProgramElementDoc> classMethods = new HashMap<String, ProgramElementDoc>();
    HashMap<String, ProgramElementDoc> classFields = new HashMap<String, ProgramElementDoc>();
    if( doc.isMethod() || doc.isField() )
    {  // fill our maps
      ClassDoc containingClass = doc.containingClass();
      for( MethodDoc m : containingClass.methods() ) {
        if( needsWriting( m ) ) {
          classMethods.put( m.name(), m );
        }
      }
      for( FieldDoc f : containingClass.fields() ) {
        if( needsWriting( f ) ) {
          classFields.put( f.name(), f );
        }
      }
    }

    // add link to each @see item
    for( SeeTag tag : getAllSeeTags( doc ) )
    {
      HashMap<String, String> map = new HashMap<String, String>();
      ProgramElementDoc ref = tag.referencedClass();
      if( tag.referencedMember() != null )
      {
        ref = tag.referencedMember();
        if( ref.isMethod() && classMethods.containsKey( ref.name() ) ) {
          // link to the member as it is in this class, instead of
          // as it is in another class
          ProgramElementDoc prior = classMethods.get( ref.name() );
          ref = prior;
        }
        else if ( ref.isField() && classFields.containsKey( ref.name() ) ) {
          ProgramElementDoc prior = classFields.get( ref.name() );
          ref = prior;
        }
      }
      if( needsWriting( ref ) )
      { // add links to things that are actually written
        map.put("name", getName( ref ));
        map.put("anchor", getAnchor( ref ));
        vars.add(map);
      }
    }

    // add link to each @see_external item
    for( Tag tag : doc.tags( Shared.i().getSeeAlsoTagName() ) )
    {
      // get xml for method
      String filename = tag.text() + ".xml";
      String basePath = Shared.i().getXMLDirectory();
      File f = new File( basePath + filename );

      if( ! f.exists() )
      {
        basePath = Shared.i().getIncludeDirectory();
        f = new File( basePath + filename );
      }

      if( f.exists() )
      {
        Document xmlDoc = Shared.loadXmlDocument( f.getPath() );
        XPathFactory xpathFactory = XPathFactory.newInstance();
        XPath xpath = xpathFactory.newXPath();

        try
        {
          String name = (String) xpath.evaluate("//name", xmlDoc, XPathConstants.STRING);
          // get anchor from original filename
          String path = f.getAbsolutePath();
          String anchorBase = path.substring( path.lastIndexOf("/")+1, path.indexOf(".xml"));
          if( name.endsWith("()") )
          {
            if( !anchorBase.endsWith("_" ) )
            {
              anchorBase += "_";
            }
          }
          String anchor = anchorBase + ".html";

          // get method name from xml
          // get anchor from method name
          HashMap<String, String> map = new HashMap<String, String>();
          map.put( "name", name );
          map.put( "anchor", anchor );
          vars.add( map );
        } catch (XPathExpressionException e)
        {
          e.printStackTrace();
        }
      }
    }

    return templateWriter.writeLoop("related.partial.html", vars);
  }

  protected static String getEvents(ProgramElementDoc doc){
    return "";
  }

}
TOP

Related Classes of writers.BaseWriter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.