Package org.jostraca

Source Code of org.jostraca.Service

/*
* Name:    Service
* Authors: Richard Rodger
*
* Copyright (c) 2000-2006 Richard Rodger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/

// package
package org.jostraca;


// import
import org.jostraca.process.ProcessManager;
import org.jostraca.process.BasicProcessManager;
import org.jostraca.process.MetaUtil;
import org.jostraca.process.ProcessException;
import org.jostraca.process.GenericPreparer;

import org.jostraca.util.Internal;
import org.jostraca.util.ErrorUtil;
import org.jostraca.util.Tracker;
import org.jostraca.util.Standard;
import org.jostraca.util.ListUtil;
import org.jostraca.util.ArgUtil;
import org.jostraca.util.BasicWayPoint;
import org.jostraca.util.WayPointRecorder;
import org.jostraca.util.StandardException;
import org.jostraca.util.RootBuildResource;
import org.jostraca.util.FileBuildResource;
import org.jostraca.util.RegExp;
import org.jostraca.util.RegExpProvider;
import org.jostraca.util.PropertySet;
import org.jostraca.util.PropertySetManager;
import org.jostraca.util.OrderedPropertySetManager;
import org.jostraca.util.PropertySetModifierManager;
import org.jostraca.util.UserMessageHandler;
import org.jostraca.util.CommandLineUserMessageHandler;

import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;



/** The <code>Service</code> class provides the top level code generation functionality of Jostraca.
*    <p>This class is used by other classes which provide a code generation service and therefore
*  provides the full functionality of Jostraca in a relatively complex API.</p>
*    <p>To use Jostraca as a component, try the {@link org.jostraca.Generator} class, which is easier to use than <code>Service</code>.</p>
*/
public class Service implements Constants {   

  // public static

  public static final String CN = Service.class.getName();
  public static final String STANDARD_TRACKER = "s";
  public static final Tracker t = Tracker.getTracker( STANDARD_TRACKER );

  public static final String  DUMP_SPEC_settings        = "settings";
  public static final String  DUMP_SPEC_template        = "template";




  // private instance

  // list of templates to build
  private List iTemplatePaths = new ArrayList();

  // template object factory - create objects which describe template properties
  private TemplateFactory iTemplateFactory = new BasicTemplateFactory();

  // manage property set precedences
  private OrderedPropertySetManager iContextPSM = null;

  // manage language PropertySets
  private PropertySetManager iLangPS = null;

  // manage property set modifiers
  private PropertySetModifierManager iPropertySetModifierManager = new PropertySetModifierManager();

  // default for command line use
  private UserMessageHandler iUserMessageHandler = new CommandLineUserMessageHandler();

  // configuration files folder
  private String iConfigFolder = Standard.EMPTY;

  // context object passed to internal code writer
  private Object iContextForTemplate = null;




  // public methods

  /** Create Service object to run code generation. */
  public Service() {
    iContextPSM = DefaultPropertySets.makeStandardOrder();
    iContextPSM.put( Service.CONF_system,   DefaultPropertySets.makeSystemPropertySet() );
    iLangPS = DefaultPropertySets.makeLang();
    setRegExpProvider( iContextPSM.get( Service.CONF_system ) );
  }


  /*
  public static void activateTracking() {
   
  }
  */

 
  /** Set user message handler.
   *  @param pUserMessageHandler UserMessageHandler instance
   */
  public void setUserMessageHandler( UserMessageHandler pUserMessageHandler ) {
    iUserMessageHandler = (UserMessageHandler) Internal.null_arg( pUserMessageHandler );
  }
 


  /** Set template paths. {@link org.jostraca.TemplatePath} objects
   *  encapsulate the concept of a template path, where the path can
   *  be a file path (foo/bar/a.jtm) or an abstract template library
   *  path (foo.bar.a).
   *  @param pTemplatePaths Paths to template files
   */
  public void setTemplatePaths( List pTemplatePaths ) {
    iTemplatePaths = (List) Internal.null_arg( pTemplatePaths );
  }



  /** Set configuration files folder.
   *  @param pConfigFolder Path to config files
   */
  public void setConfigFolder( String pConfigFolder ) {
    iConfigFolder = ( String ) Internal.null_arg( pConfigFolder );
  }



  /** Set context for template to use.
   *  @param pContextForTemplate context object
   */
  public void setContextForTemplate( Object pContextForTemplate ) {
    iContextForTemplate = ( Object ) Internal.null_arg( pContextForTemplate );
  }



  /** Add a PropertySet by name. Unknown names are ignored.
   *  @param pName        Name of PropertySet
   *  @param pPropertySet PropertySet to add
   */
  public void addPropertySet( String pName, PropertySet pPropertySet ) {
    PropertySet propSet = pPropertySet;

    if( ErrorUtil.not_null( pName  ) ) {
      if( ErrorUtil.is_null( propSet ) ) {
        propSet = new PropertySet(); // add an empty one
      }
      iContextPSM.put( pName, propSet );
      t.track( ".addPropertySet:", pName+" size:"+propSet.size() );
      if( CONF_system.equals( pName ) ) {
        setRegExpProvider( propSet );
      }
    }
  }
   

  public PropertySet getCurrentConfig() {
    return iContextPSM.merge();
  }


  /** Loads system configuration from specified folder.
   *   
   */
  public void loadConfigFromFolder( File pConfigFolder, ArrayList pAdditionalConfig ) {
    File configFolder = (File) Internal.null_arg( pConfigFolder );
    t.track( ".loadConfigFromFolder:", configFolder );

    PropertySet systemPS = null;

    setConfigFolder( configFolder.getAbsolutePath() );

    // REVIEW: need a better way to do this!
    File systemConfFile = new File( iConfigFolder, FILE_NAME_system_conf );
    systemPS = loadBaseConfigFiles( systemConfFile );

    PropertySet addps = loadAdditionalConfigFiles( pAdditionalConfig );
    systemPS.overrideWith( addps );

    PropertySet local           = new PropertySet();
    File        localConfigFile = new File( pConfigFolder, systemPS.get( Property.jostraca_LocalConfigFileName ) );
    if( localConfigFile.exists() && !localConfigFile.isDirectory() ) {
      local.load( localConfigFile  );
      systemPS.overrideWith( local );
    }
    else {
      // if local.conf does not exist, ignore
    }

    Tools.produceJostracaLocation( systemPS, systemConfFile );

    addPropertySet( CONF_system, systemPS );
  }


  /** Load Additional config files and apply to system config.
   */
  private PropertySet loadAdditionalConfigFiles( ArrayList pAdditionalConfigFiles  ) {
    PropertySet result = new PropertySet();
    int numAddConf = pAdditionalConfigFiles.size();
    for(int addConfI = 0; addConfI < numAddConf; addConfI++) {
      String      path    = (String) pAdditionalConfigFiles.get( addConfI );
      PropertySet addConf = new PropertySet();
      addConf.load( new File(path) );
      result.overrideWith( addConf );
    }
    return result;
  }


  /** Build code from prespecified list of TemplatePath objects.
   *  @return List of Template objects
   */
  public List build() {
    List templates = build( iTemplatePaths );
    return templates;
  }



  /** Build list of {@link org.jostraca.Template} or {@link org.jostraca.TemplatePath} objects.
   *  @return List of Template objects
   */
  public List build( List pTemplateObjects ) {
    List tmlist  = new ArrayList();
    List tmolist = (List) Internal.null_arg( pTemplateObjects );
    if( 0 < tmolist.size() ) {
      if( tmolist.get(0) instanceof TemplatePath ) {
        tmlist = buildTemplatePaths( tmolist );
      }
      else {
        tmlist = buildTemplates( tmolist );
      }
    }
    return tmlist;
  }



  /** Build template from template path.
   *  @return List of Template objects
   */
  public List build( TemplatePath pTemplatePath ) {
    TemplatePath tmpath     = (TemplatePath) Internal.null_arg( pTemplatePath );
    List         tmpathlist = ListUtil.make( tmpath );
    List         tmlist     = buildTemplatePaths( tmpathlist );
    return tmlist;
  }


  /** Build templates from list of template paths.
   *  @return List of Template objects
   */
  public List buildTemplatePaths( List pTemplatePaths ) {
    List templatepaths = (List) Internal.null_arg( pTemplatePaths );
    t.track( "buildTemplatePaths:", ""+templatepaths );
   
    List tmlistin = new ArrayList();
    int  numtmp = templatepaths.size();
    for( int tmpI = 0; tmpI < numtmp; tmpI++ ) {
      TemplatePath tmpath = (TemplatePath) templatepaths.get(tmpI);
      if( null == tmpath ) {
        throw ServiceException.CODE_null_tmpath( "index", String.valueOf(tmpI) );
      }
      else {
        Template tm = makeTemplate( tmpath );
        tmlistin.add( tm );
      }
    }

    List tmlist = buildTemplates( tmlistin );
    return tmlist;
  }


  /** Build a template.
   *  @return Template object
   */
  public Template build( Template pTemplate ) {
    Template template  = (Template) Internal.null_arg( pTemplate );
    List     tmlistin  = ListUtil.make( template );
    List     tmlist    = buildTemplates( tmlistin );
    if( 0 < tmlist.size() ) {
      return (Template) tmlist.get(0);
    }
    else {
      return pTemplate;
    }
  }



  /** Build a list of templates.
   *  @return List of Template objects
   */
  public List buildTemplates( List pTemplates ) {
    List templates = (List) Internal.null_arg( pTemplates );
    t.track( "buildTemplates:", ""+templates );

    ErrorUtil.not_null( iUserMessageHandler );

    PropertySet  contextps = iContextPSM.merge();

    // process templates
    ArrayList tmlist = new ArrayList();
    for( Iterator tmT = pTemplates.iterator(); tmT.hasNext(); ) {
      Template tm       = (Template) tmT.next();

      // set config - user may have created Template object directly
      List psnL = iContextPSM.listNames();
      for( Iterator psnT = psnL.iterator(); psnT.hasNext(); ) {
        String psn = (String) psnT.next();
        PropertySet tmps = tm.getPropertySet(psn);
        tmps.inheritFrom( iContextPSM.get( psn ) );
        tm.setPropertySet( psn, tmps );
      }

      // HACK: as we need some property mods before checkUpToDate
      GenericPreparer.handleCodeWriterName( tm );

      // drop templates that are up-to-date
      boolean  uptodate = checkUpToDate( tm );
      if( uptodate ) {
        WayPointRecorder.add( BasicWayPoint.GenerationUptodate.make( tm.getTemplatePath().getTemplatePath() ) );
        iUserMessageHandler.info"Uptodate:",  tm.getTemplatePath().getTemplateName() );        
      }
      else {
        tmlist.add( tm );
      }
    }

    t.track( "buildTemplates attempt:", ""+tmlist );   

    ProcessManager pm = makeProcessManager( contextps );
    pm.setUserMessageHandler( iUserMessageHandler );
    pm.process( tmlist );

    // return full list, including those not generated
    return templates;
  }



  /** Create a template object from a template path */
  public Template makeTemplate( TemplatePath pTemplatePath ) {
    TemplatePath tmpath = (TemplatePath) Internal.null_arg( pTemplatePath );
    t.track( "TemplatePath", pTemplatePath );

    PropertySet contextps = iContextPSM.merge();
    t.track( "build.TemplatePath.contextps.size", contextps.size() );

    // FIX: consider sending ps to tmpath instead to hide this
    String[] tmsearchpath = contextps.getList( Property.main_TemplatePath, Standard.COMMA );
    tmpath.resolve( tmsearchpath );

    // REVIEW: l10n
    iUserMessageHandler.info"Template:",      tmpath.getTemplateName() );
    iUserMessageHandler.debug( "Template Path:", tmpath.getTemplatePath() );

    Template template = iTemplateFactory.createTemplate( pTemplatePath, iContextPSM );

    if( null != iContextForTemplate ) {
      template.setContext( iContextForTemplate );
    }

    return template;
  }




  // REVIEW: Jostraca should use this
  /** Load system and local configuration */
  public PropertySet loadBaseConfigFiles( File pSystemConfigFile ) {
    File    systemConfigFile   = (File) Internal.null_arg( pSystemConfigFile );
    boolean systemConfigExists = systemConfigFile.exists();

    iContextPSM.load( CONF_system, systemConfigFile, PropertySetManager.USE_DEFAULT_IF_FILE_DOES_NOT_EXIST );
    PropertySet system = iContextPSM.get( CONF_system );

    File configFolder = systemConfigFile.getParentFile();
    if( null == configFolder ) {
      configFolder = new File( "" );
    }
    PropertySet local           = new PropertySet();
    File        localConfigFile = new File( configFolder, system.get( Property.jostraca_LocalConfigFileName ) );
    if( localConfigFile.exists() && !localConfigFile.isDirectory() ) {
      local.load( localConfigFile  );
    }

    system.overrideWith( local );

    // only set jostraca.Location if using configuration from disk
    if( systemConfigExists ) {
      Tools.produceJostracaLocation( system, systemConfigFile );
    }

    return system;
  }



  /** return a List of TemplatPath objects */
  public static List buildTemplateListFromFile( File pFile ) {
    File           tlf       = (File) Internal.null_arg( pFile );
    try {
      ArrayList      templates = new ArrayList();
      FileReader     fr        = new FileReader( pFile );
      BufferedReader br        = new BufferedReader( fr );

      String line = null;
      while( null != ( line = br.readLine() ) ) {
        line = line.trim();
        if( 0 == line.length() ) {
          continue;
        }
        BasicTemplatePath btp = new BasicTemplatePath( line, tlf.getAbsolutePath() );
        templates.add( btp );
      }
      fr.close();

      return templates;
    }
    catch( StandardException se ) {
      throw se;
    }
    catch( Exception e ) {
      throw new TemplateException( TemplateException.CODE_load_file, pFile );
    }
  }



  // FIX: funny place for this - surely Jostraca is better
  /** Set dump options from dump specification argument.
   *  expected format: propertyset,template,... etc
   *  @param pDumpSpec dump specification String
   *  @param pCmdLineOptions dump settings placed here
   */
  public static void parseDumpSpec( String pDumpSpec, PropertySet pCmdLineOptions ) {
    if( -1 != pDumpSpec.indexOf( DUMP_SPEC_settings ) ) {
      pCmdLineOptions.set( Property.main_DumpPropertySet, Standard.YES );
    }
    if( -1 != pDumpSpec.indexOf( DUMP_SPEC_template ) ) {
      pCmdLineOptions.set( Property.main_DumpTemplate, Standard.YES );
    }
  }


  /** Activate tracking of internal processing to a log file. */
  public static void activateTracking( File pTrackFile ) {
    File trackF = (File) Internal.null_arg( pTrackFile );

    try {
      File trackParentF = trackF.getParentFile();
      if( !trackParentF.exists() ) {
        trackParentF.mkdirs();
      }
      Tracker.activate( pTrackFile );
    }
    catch( Exception e ) {
      throw new ServiceException( ServiceException.CODE_bad_track_file, trackF );
    }
  }



  /** Deactivate tracking of internal processing. */
  public static void deactivateTracking() {
    Tracker.deactivate();
  }



  // private methods

  private ProcessManager makeProcessManager( PropertySet pPropertySet ) {
    ProcessManager pm = new BasicProcessManager( pPropertySet );
    return pm;
  }


  private List prepare( List pTemplatePaths ) {
    List templatePaths = (List) Internal.null_arg( pTemplatePaths );

    ArrayList    templates = new ArrayList();
    PropertySet  contextps = iContextPSM.merge();

    for( Iterator tpT = templatePaths.iterator(); tpT.hasNext(); ) {
      TemplatePath tp = (TemplatePath) tpT.next();
     
      tp.resolve( contextps.getList( Property.main_TemplatePath, Standard.COMMA ) );

      // REVIEW: l10n
      iUserMessageHandler.info"Template:",      tp.getTemplateName() );
      iUserMessageHandler.debug( "Template Path:", tp.getTemplatePath() );

      Template template = iTemplateFactory.createTemplate( tp, iContextPSM );

      if( null != iContextForTemplate ) {
        template.setContext( iContextForTemplate );
      }

      templates.add( template );
    }

    return templates;
  }



  /** Check uptodate status. Try to determine generated files
   *  using build metadata, if available, and test last modified data
   *  against any CodeWriter options that are files, if found.
   *  Return true if uptodate.
   */
  private boolean checkUpToDate( Template pTemplate ) {

    PropertySet tmps = pTemplate.getMergedPropertySet();

    // assume not uptodate
    boolean uptodate = false;

    try {
      TemplatePath tp = pTemplate.getTemplatePath();

      // some properties force regenerate
      if( tmps.isYes( Property.main_CompileCodeWriter )
          || tmps.isYes( Property.main_ExecuteCodeWriter )
          || tmps.isNo( Property.main_EnableMeta ) ) {
        uptodate = false;
        iUserMessageHandler.debug( "Not uptodate:",
                                   Property.main_CompileCodeWriter+":"+tmps.get( Property.main_CompileCodeWriter )
                                   + "  " + Property.main_ExecuteCodeWriter+":"+tmps.get( Property.main_ExecuteCodeWriter )
                                   + "  " + Property.main_EnableMeta+":"+tmps.get( Property.main_EnableMeta ) );
      }

      // check meta data
      else {
        PropertySet meta    = null;
        boolean     proceed = true;

        // load meta data
        if( proceed ) {
          try {
            meta = MetaUtil.loadMetaData( pTemplate );
          }
          catch( ProcessException te ) {
            if( ProcessException.CODE_load_meta == te.getCode() ) {
              // meta data could not be loaded - not an error
            }
            else {
              ErrorUtil.nonFatalMsg( te.getMessage() );
            }

            uptodate = false;
            iUserMessageHandler.debug( "Not uptodate:", "unable to read meta data from previous generate." );
            proceed = false;
          }
        }
         
        // if template properties are different, regenerate
        if( proceed ) {

          // FIX: props from template only
          String ctmps  = Tools.normaliseTemplatePropertySet( pTemplate.getPropertySet( Constants.CONF_template ) );
          String mtmps = meta.get( Property.jostraca_template_properties );
          t.track( "current template ps", ctmps );
          t.track( "prev template ps", mtmps );
          if( !ctmps.equals( mtmps ) ) {
            iUserMessageHandler.debug( "Not uptodate:", "template properties were different from previous generate." );
            uptodate = false;
            proceed  = false;

            //System.out.println( "ctmps:["+ctmps+"]" );
            //System.out.println( "mtmps:["+mtmps+"]" );
          }
        }

        // if cmd line is different, regenerate
        if( proceed ) {
          String co  = normaliseCmdLine( tmps.get( Property.main_CodeWriterOptions ) );
          String mco = normaliseCmdLine( meta.get( Property.main_CodeWriterOptions ) );
          t.track( "cmd line", co );
          t.track( "prev cmd line", mco );
          if( !co.equals( mco ) ) {
            iUserMessageHandler.debug( "Not uptodate:", "command line was different from previous generate." );
            uptodate = false;
            proceed  = false;
          }
        }

        // test resource files
        if( proceed ) {
          RootBuildResource rbr               = new RootBuildResource();
          String[]          generatedFiles    = meta.getList( Property.jostraca_GeneratedFiles, Standard.COMMA );
          int               numGF             = generatedFiles.length;

          // this assumes that we actually want to generate files, not just compile the template
          // however, the CodeBuilder should ensure that no unnecessary compilation is done in any case
          // this logic will probably cause unnecessary reparsing of the template, but it's good to err
          // on the side of regenerating to avoid false negatives
          if( 0 == numGF ) {
            iUserMessageHandler.debug( "Not uptodate:", "no files generated previously." );
            uptodate = false;
          }

          // there were previously generated files, so we can test the resources against them for uptodateness
          else {
            String[]          tm_resourceFiles  = meta.getList( Property.jostraca_FileBuildResources, Standard.COMMA );
            int               num_tmRF          = tm_resourceFiles.length;

            File[]            cmd_resourceFiles = parseResourceFiles( tmps.get( Property.main_CodeWriterOptions ),
                                                                      tmps.get( Property.main_WorkFolder ) );
            int               num_cmdRF         = cmd_resourceFiles.length;

            // check against template
            for( int gfI = 0; gfI < numGF; gfI++ ) {
              FileBuildResource fbr = new FileBuildResource( new File( tp.getTemplatePath() ),
                                                             new File( generatedFiles[gfI] ) );
              rbr.add( fbr );
            }

            // check against template resources
            for( int rfI = 0; rfI < num_tmRF; rfI++ ) {
              for( int gfI = 0; gfI < numGF; gfI++ ) {
                FileBuildResource fbr = new FileBuildResource( new File( tm_resourceFiles[rfI] ),
                                                               new File( generatedFiles[gfI] ) );
                rbr.add( fbr );
              }
            }

            // check against cmd resources
            for( int rfI = 0; rfI < num_cmdRF; rfI++ ) {
              for( int gfI = 0; gfI < numGF; gfI++ ) {
                FileBuildResource fbr = new FileBuildResource( cmd_resourceFiles[rfI],
                                                               new File( generatedFiles[gfI] ) );
                rbr.add( fbr );
              }
            }

            uptodate = rbr.uptodate();         
            proceed  = false;

            if( uptodate ) {
              iUserMessageHandler.debug( "Uptodate:", rbr.toString() );
            }
            else {
              iUserMessageHandler.debug( "Not uptodate:", rbr.toString() );
            }
          }
        }
      }

      t.track( "checkUpToDate", uptodate );
      return uptodate;
    }
    catch( Exception e ) {
      ErrorUtil.nonFatalMsg( e );
      return false;
    }
  }



  // FIX: convert to List
  /** Find the CodeWriter options which are files */
  private File[] parseResourceFiles( String pCodeWriterOptions, String pWorkFolder ) {
    Vector   resourceFilesV = new Vector();
    String[] options        = ArgUtil.splitQuoted( pCodeWriterOptions );
    int      numOptions     = options.length;
    String   option         = Standard.EMPTY;
    File     f              = null;

    for( int optionI = 0; optionI < numOptions; optionI++ ) {
      option = options[optionI];
      f      = null;

      // relative to work folder
      f = new File( pWorkFolder, option );
      if( f.exists() ) {
        resourceFilesV.addElement( f );
        continue;
      }

      // absolute path
      f = new File( option );
      if( f.exists() ) {
        resourceFilesV.addElement( f );
        continue;
      }
    }

    File[] files = new File[ resourceFilesV.size() ];
    resourceFilesV.copyInto( files );
    return files;
  }


 
  /** Always use forward slash */
  private String normaliseCmdLine( String pCmdLine ) {
    String cmdline = pCmdLine.replace( Standard.CHAR_BACKSLASH, Standard.CHAR_FORWARDSLASH );
    cmdline = cmdline.trim();
    return cmdline;
  }



  private void setRegExpProvider( PropertySet pSystemPS ) {
    String repStr = pSystemPS.get( Property.jostraca_RegExpProvider );

    try {
      Class          repClass = Class.forName( repStr );
      RegExpProvider rep      = (RegExpProvider) repClass.newInstance();
      RegExp.setRegExpProvider( rep );
    }
    catch( Exception e ) {
      throw new ServiceException( ServiceException.CODE_invalid_regexpprovider, repStr, e );
    }

  }
   
}
TOP

Related Classes of org.jostraca.Service

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.