Package kameleon.gui.model

Source Code of kameleon.gui.model.GenerationModel$GenerationThread

/*
* Copyright (c) 2012, Fromentin Xavier, Schnell Michaël, Dervin Cyrielle, Brabant Quentin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*      * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*      * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*      * The names of its contributors may not be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Fromentin Xavier, Schnell Michaël, Dervin Cyrielle OR Brabant Quentin
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package kameleon.gui.model;

import java.io.File;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

import javax.swing.SwingWorker;

import kameleon.document.Document;
import kameleon.exception.InvalidPlugInException;
import kameleon.exception.KameleonException;
import kameleon.gui.exception.InvalidGenerationException;
import kameleon.gui.model.GenerationMessage.GenerationState;
import kameleon.gui.model.GenerationMessage.State;
import kameleon.plugin.Analyzer;
import kameleon.plugin.Generator;
import kameleon.plugin.PlugInInfo;
import kameleon.util.IOPlugIn;

/**
* Model responsible for the generation process.
*
* <p>//TODO ADD A LOT MORE DETAILS !
*
* @author    Brabant Quentin, Dervin Cyrielle, Fromentin Xavier, Schnell Michaël
* @version    1.0
*/
public class GenerationModel extends FileModel {

  /**
   * Flag indicating that a generator has just been added
   * or removed.
   */
  protected boolean generatorAddedOrRemoved ;

  /**
   * Flag indicating that the generation process has just
   * finished.
   */
  protected boolean generationFinished ;

  /**
   * Builds an instance with the given options.
   *
   * <p>Reads the history and the the configuration files
   * to find the installed plug-ins.
   *
   * @param   debugMode
   *       flag indicating whether the debug mode
   *       should be activated ({@code true} means
   *       activated)
   */
  public GenerationModel(boolean debugMode) {
    super(debugMode) ;
    this.generatorAddedOrRemoved = false ;
    this.selectedGenerators = new ArrayList<String>() ;
    try {
      this.initializePlugIns() ;
    } catch(KameleonException ke) {
      this.displayDebugInformation(ke) ;
    }// try
  }// GenerationModel(boolean)

  /**
   * Builds an instance with default options.
   */
  public GenerationModel() {
    this(DEFAULT_DEBUG_MODE) ;
  }// GenerationModel()

  /**
   * Generates the absolute file paths for the files generated by
   * the selected generators.
   *
   * <p>If only generator is selected, the model will only check is
   * the given file extension is valid. If not, a valid (default) one
   * is appended to the file name.
   *
   * <p>If multiple generators are selected, the software will
   * append {@code "_<format name>"} to each file name before the
   * valid extension. Same as before, if the given extension is not
   * valid, a default one is added.
   *
   * //TODO Add an example
   *
   * @return  Array containing the generated names, the order of the
   *       names is the same as the order of the generators
   *///TODO Review null pointer
  protected String[] generateFileNames() {
    if (this.selectedGenerators.size() == 1) {
      return new String[]{generateName(
          this.outputFile.getAbsolutePath(),
          this.getGenerator(this.selectedGenerators.get(0)))} ;
    }// if
    String[] names = new String[this.selectedGenerators.size()] ;
    Iterator<String> iter = this.selectedGenerators.iterator() ;
    String baseName = this.outputFile.getAbsolutePath() ;
    for(int n = 0; iter.hasNext(); ++n) {
      names[n] = generateExtendedName(baseName,
          this.getGenerator(iter.next())) ;
    }// for
    return names ;
  }// generateFileNames()

  /**
   * Generates the file name for a given generator using the provided
   * name. Checks if the given extension is valid for the given
   * generator and if is not, appends a valid one to the file name.
   * If the given generator has no valid extension, the file name
   * is left unchanged.
   *
   * @param   baseName
   *       absolute path of the wanted file
   *
   * @param   generatorInfo
   *       generator of the file whose name is created
   *
   * @return  Absolute path for the file
   *///TODO Review null pointers
  protected static String generateName(String baseName, PlugInInfo generatorInfo) {
    String ext = getExtension(baseName) ;
    StringBuilder name = new StringBuilder(baseName) ;
    if (!isValidExtension(ext, generatorInfo)) {
      String[] validExtensions = generatorInfo.getExtensions() ;
      if (validExtensions.length > 0) {
        name.append('.') ;
        name.append(validExtensions[0]) ;
      }// if
    }// if
    return name.toString() ;
  }// generateName(String, PlugInInfo)

  /**
   * Generates the file name for a given generator using the provided
   * name. Checks if the given extension is valid for the given
   * generator and if is not, appends a valid one to the file name.
   * If the given generator has no valid extension, the file name
   * is left unchanged. Also adds {@code "_<format name>"} before
   * the (valid) extension. This is done in order to prevent multiple
   * generators from generate the same file.
   *
   * @param   baseName
   *       absolute path of the wanted file
   *
   * @param   generatorInfo
   *       generator of the file whose name is created
   *
   * @return  Absolute path for the file
   */
  protected static String generateExtendedName(String baseName, PlugInInfo generatorInfo) {
    String ext = getExtension(baseName) ;
    StringBuilder name = new StringBuilder(baseName) ;
    if (!isValidExtension(ext, generatorInfo)) {
      name.append('_') ;
      name.append(generatorInfo.getFormatName()) ;
      String[] validExtensions = generatorInfo.getExtensions() ;
      if (validExtensions.length > 0) {
        name.append('.') ;
        name.append(validExtensions[0]) ;
      }// if
    } else {
      int posDot = baseName.lastIndexOf('.') ;
      name.insert(posDot, generatorInfo.getFormatName()) ;
      name.insert(posDot, '_') ;
    }// if
    return name.toString() ;
  }// generateExtendedName(String, PlugInInfo)

  /**
   * Indicates if the given extension is commonly used by the given
   * plug-in (analyzer or generator).
   *
   * @param   extension
   *       extension which should be tested
   *
   * @param   generatorInfo
   *       plug-in for which the extension should be tested
   *
   * @return  {@code true} if the plug-in commonly uses the given
   *       extension, {@code false} otherwise
   *
   * @see    PlugInInfo
   */
  protected static boolean isValidExtension(String extension, PlugInInfo generatorInfo) {
    boolean valid = (generatorInfo.getExtensions().length == 0) ;
    for(String ext : generatorInfo.getExtensions()) {
      valid = ext.equals(extension) ;
      if (valid) break ;
    }// if
    return valid ;
  }// isValidExtension(String, PlugInInfo)

  /**
   * Returns an instance of the "main" class of the given plug-in.
   *
   * @param  info
   *       information about he plug-in whose instance is requested
   *
   * @param   parentFolder
   *       absolute path the of the folder which contains the
   *       java archive of the plug-in
   *
   * @param   targetClass
   *       either {@code Analyzer.class} of {@code Generator.class}
   *
   * @return  Instance of the main class of the given plug-in
   *
   * @throws   InvalidPlugInException
   *       If the main class could not be instantiated
   *
   * @see    PlugInInfo
   * @see    Analyzer
   * @see    Generator
   *///TODO Add exception for illegal argument
  private static <T> T getInstance(PlugInInfo info, String parentFolder, Class<T> targetClass)
      throws InvalidPlugInException {
    try {
      ClassLoader loader = IOPlugIn.loadPlugIn(info, parentFolder) ;
      loader.loadClass(info.getPlugInClass()) ;
      Class<?> plugInClass = Class.forName(info.getPlugInClass(), true, loader) ;
      Class<? extends T> plClass = plugInClass.asSubclass(targetClass) ;
      Constructor<? extends T> constructor = plClass.getConstructor() ;
      return constructor.newInstance() ;
    } catch (Exception e) {
      /* === Possible exceptions ===
       *  - ClassNotFoundException
       *  - IllegalAccessException
       *  - IllegalArgumentException
       *  - InstantiationException
       *  - InvocationTargetException
       *  - NoSuchMethodException
       *  - SecurityException
       * === ------------------- === */
      throw new InvalidPlugInException(info, e) ;
    }// try
  }// getInstance(PlugInInfo, String, Class<T>)

  /**
   * Launches the analyzing process with the given analyzer.
   *
   * @param   analyzer
   *       plug-in used to analyzed the currently selected file
   *
   * @return  instance of {@code Document} resulting from the
   *       analyzing process
   *
   * @throws  InvalidGenerationException
   *       if the generation is impossible
   *
   * @throws   KameleonException
   *       if an error occurred while analyzing the selected file
   */
  protected Document launchAnalyzer(PlugInInfo analyzer) throws KameleonException {
    if (!this.generationIsPossible()) {
      throw new InvalidGenerationException() ;
    }// if
    return GenerationModel.launchAnalyzer(analyzer,
        this.getSelectedFileInfo().getPath()) ;
  }// launchAnalyzer(PlugInInfo)

  /**
   * Launches the analyzing process with the given analyzer on the
   * given file.
   *
   * @param   analyzer
   *       plug-in used to analyzed the given file
   *
   * @param  path
   *       absolute path of the analyzed file
   *
   * @return  instance of {@code Document} resulting from the
   *
   * @throws   KameleonException
   *       if an error occurred while analyzing the given file
   */
  public static Document launchAnalyzer(PlugInInfo analyzer, String path)
      throws KameleonException {
    Analyzer plugIn = getInstance(analyzer, ANALYSER_FOLDER, Analyzer.class) ;
    return plugIn.analyze(path) ;
  }// launchAnalyzer(PlugInInfi, String)

  /**
   * Launches the generation of the given file with the given
   * generator.
   *
   * @param   generator
   *       plug-in used to generate the given file
   *
   * @param  document
   *       source for the content of the generated file
   *
   * @param  targetFile
   *       absolute path of the generated file
   *
   * @throws   KameleonException
   *       if an error occurred while generating the file
   */
  public static void launchGenerator(PlugInInfo generator, Document document, String targetFile)
      throws KameleonException {
    Generator plugIn = getInstance(generator, GENERATOR_FOLDER, Generator.class) ;
    plugIn.generate(document, targetFile) ;
  }// launchGeneration(PlugInInfo, Document, String)

  /**
   * Indicates if a generator has just been added or removed.
   *
   * @return  {@code true} if a generator has just been added
   *       or removed, {@code false} otherwise
   */
  public boolean generatorAddedOrRemoved() {
    return this.generatorAddedOrRemoved ;
  }// generatorAddedOrRemoved()
 
  /**
   * Adds a new file. If the file is a plug-in installation file,
   * the model will attempt to install the plug-in. Otherwise
   * the model will add the file to history and select it.
   *
   * @param  newFile
   *       added file
   */
  @Override
  public void addFile(File newFile) {
    // Test if the added file is a plug-in
    String extension = getExtension(newFile) ;
    if (PLUGIN_EXTENSION.equals(extension)) {
      // Install the plug-in
      this.addPlugIn(newFile) ;
      return ;
    }// if
    super.addFile(newFile) ;
  }// addFile(File)

  /**
   * {@inheritDoc}
   *
   * @return  {@code PlugInInfo} about the newly added plug-in
   *       or {@code null} if the plug-in could not be added
   */
  @Override
  public PlugInInfo addPlugIn(File plugin) {
    PlugInMessage pmsg = new PlugInMessage(true, null) ;
    try {
      pmsg.setMessage(ADD_PLUG_IN_START_MESSAGE) ;
      this.addMessage(pmsg) ;

      PlugInInfo info = super.addPlugIn(plugin) ;

      pmsg.setState(PlugInMessage.State.SUCCESS) ;
      pmsg.setMessage(
          info.isAnalyzer() ? ADD_ANALYZER_SUCESS_MESSAGE
                        : ADD_GENERATOR_SUCESS_MESSAGE,
          info.getFormatName()) ;
     
      this.generatorAddedOrRemoved = !info.isAnalyzer() ;
      this.updateMessage(pmsg) ;
      this.generatorAddedOrRemoved = false ;

      return info ;
    } catch (KameleonException ke) {
      this.displayDebugInformation(ke) ;
      pmsg.setState(PlugInMessage.State.ERROR) ;
      pmsg.setMessage(ADD_PLUG_IN_ERROR_MESSAGE) ;
      this.updateMessage(pmsg) ;
    }// try

    return null ;
  }// adPlugIn(File)

  /**
   * {@inheritDoc}
   *
   * @return  {@code PlugInInfo} about the newly added plug-in
   *       or {@code null} if the plug-in could not be added
   */
  @Override
  public PlugInInfo addPlugIn(String pluginPath) {
    return this.addPlugIn(new File(pluginPath)) ;
  }// addPlugIn(String)

  /**
   * {@inheritDoc}
   */
  @Override
  public void removeAnalyzer(String analyzerId) {
    this.removePlugIn(this.getAnalyzer(analyzerId)) ;
  }// removeAnalyzer(String)

  /**
   * {@inheritDoc}
   */
  @Override
  public void removeGenerator(String generatorId) {
                this.removeSelectedFormat(generatorId);
    this.removePlugIn(this.getGenerator(generatorId)) ;
  }// removeGenerator(String)

  /**
   * {@inheritDoc}
   */
  @Override
  public void removePlugIn(PlugInInfo info) {
    PlugInMessage pmsg = new PlugInMessage(false, null) ;
    try {
      pmsg.setMessage(
          info.isAnalyzer() ? REMOVE_ANALYZER_START_MESSAGE
                      : REMOVE_GENERATOR_START_MESSAGE,
          info.getFormatName()) ;
      this.addMessage(pmsg) ;

      super.removePlugIn(info) ;

      pmsg.setState(PlugInMessage.State.SUCCESS) ;
      pmsg.setMessage(
          info.isAnalyzer() ? REMOVE_ANALYZER_SUCESS_MESSAGE
                        : REMOVE_GENERATOR_SUCESS_MESSAGE,
          info.getFormatName()) ;

      this.generatorAddedOrRemoved = true ;
      this.updateMessage(pmsg) ;
      this.generatorAddedOrRemoved = false ;
    } catch (KameleonException ke) {
      this.displayDebugInformation(ke) ;
      pmsg.setState(PlugInMessage.State.ERROR) ;
      pmsg.setMessage(
        info.isAnalyzer() ? REMOVE_ANALYZER_ERROR_MESSAGE
                    : REMOVE_GENERATOR_ERROR_MESSAGE,
        info.getFormatName()) ;
      this.updateMessage(pmsg) ;
    }// try
  }// removePlugIn(PlugInInfo)

  /**
   * Launches generation in a new thread using the options of the
   * given message.
   *
   * @param   message
   *       information about the generation
   */
  public void fastGeneration(GenerationMessage message) {
    message.initializeStates() ;
    this.updateMessage(message) ;
    new GenerationThread(this, message).execute() ;
  }// fastGeneration(GenerationMessage)

  /**
   * Launches the generation for the selected file with the
   * selected generators. If the generation is impossible,
   * this function does nothing.
   */
  public void launchGeneration() {
    if (this.generationIsPossible()) {
      // Initialize message
      GenerationMessage message = new GenerationMessage(
          this.getSelectedFileInfo()) ;
      message.setLastGeneration(new Date()) ;// now
                        System.out.println(this.selectedGenerators);
      message.setTargetFormatId(this.selectedGenerators.toArray(
          new String[this.selectedGenerators.size()])) ;
      message.setTargetPaths(this.generateFileNames()) ;
      this.addMessage(message) ;
     
      // Do the actual generation
      this.fastGeneration(message) ;

      // Update hisotry
      FileInfo current = this.getSelectedFileInfo() ;
      current.setLastGeneration(message.getLastGeneration()) ;
      current.setTargetFormatId(message.getTargetFormatId()) ;
      current.setTargetPaths(message.getTargetPath()) ;
      this.writeHistory() ;
    }// if
  }// launchGeneration()

  /**
   * Indicates whether the generation process has just finished.
   *
   * @return  {@code true} if the generation process has just
   *       finished, {@code false} otherwise
   */
  public boolean generationFinished() {
    return this.generationFinished ;
  }// generationFinished()

  /**
   * Notifies the observers that the current generation process
   * has just finished.
   *
   * @param   message
   *       message associated with the finished generation
   */
  public void notifyGenerationFinished(Message message) {
    this.generationFinished = true ;   
    this.updateMessage(message) ;   
    this.generationFinished = false
  }// notifyGenerationFinished(Message)

  /**
   * Subclass of {@link SwingWorker} used to host the generation
   * process.
   *
   * @author  Schnell Michaël
   * @version  1.0
   */
  private class GenerationThread extends SwingWorker<Void, Void> {

    /**
     * Model of the graphical interface.
     */
    private GenerationModel model ;
   
    /**
     * Message associated with this generation process.
     */
    private GenerationMessage message ;

    /**
     * Builds an instance with the given values.
     *
     * @param   model
     *       model of the graphical interface
     *
     * @param   message
     *       message containing the information about the
     *       generation process
     */
    public GenerationThread(GenerationModel model, GenerationMessage message) {
      super();
      this.model = model ;
      this.message = message ;
    }// GenerationThread(GenerationModel, GenerationMessage)

    /**
     * Executes the generation of the files requested in the
     * given message as a background task.
     */
    @Override
    protected Void doInBackground() {
      int rowIndex = 0 ;     
      String[] outputFiles = this.message.getTargetPath() ;
      String[] genIds = this.message.getTargetFormatId() ;
      boolean fileNotGenerated = false ;
      try {
        PlugInInfo analyzer = this.model.getAnalyzer(
            this.message.getIdFormat()) ;
        Document document = GenerationModel.launchAnalyzer(
            analyzer, this.message.getPath()) ;

        this.message.setState(GenerationState.GENERATING) ;
        this.model.updateMessage(this.message) ;
       
        for(rowIndex=0; rowIndex<genIds.length; ++rowIndex) {
          this.message.setState(rowIndex, State.CONVERTING) ;
          this.model.updateMessage(this.message) ;

          String genId = genIds[rowIndex] ;
          PlugInInfo generator = this.model.getGenerator(genId) ;
          try {
            GenerationModel.launchGenerator(
                generator, document, outputFiles[rowIndex]) ;

            this.message.setState(rowIndex, State.CONVERTED) ;
          } catch (KameleonException ke) {
            this.message.setState(rowIndex, State.ERROR) ;
            fileNotGenerated = true ;
          }// try
          this.model.updateMessage(this.message) ;
        }// for
       
        this.message.setLastGeneration(new Date()) ;
        if (fileNotGenerated) {
          this.message.setState(GenerationState.PARTIAL_SUCESS;
        } else {
          this.message.setState(GenerationState.COMPLETE_SUCESS;
        }// if
        this.model.updateMessage(this.message) ;
      } catch (KameleonException ke) {
        while (rowIndex < genIds.length) {
          this.message.setState(rowIndex, State.ERROR) ;
          rowIndex++ ;
        }// while
        this.message.setState(GenerationState.ERROR) ;
        this.model.updateMessage(this.message) ;
        this.model.displayDebugInformation(ke) ;
      }// try
      this.model.notifyGenerationFinished(this.message) ;
     
      return null ;
    }// doInBackground()
   
  }// class GenerationProcess

}// class Model
TOP

Related Classes of kameleon.gui.model.GenerationModel$GenerationThread

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.