Package pt.webdetails.cdf.dd.model.meta.reader.cdexml.fs

Source Code of pt.webdetails.cdf.dd.model.meta.reader.cdexml.fs.XmlFsPluginModelReader

/*!
* Copyright 2002 - 2014 Webdetails, a Pentaho company.  All rights reserved.
*
* This software was developed by Webdetails and is provided under the terms
* of the Mozilla Public License, Version 2.0, or any later version. You may not use
* this file except in compliance with the license. If you need a copy of the license,
* please go to  http://mozilla.org/MPL/2.0/. The Initial Developer is Webdetails.
*
* Software distributed under the Mozilla Public License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or  implied. Please refer to
* the license for the specific language governing your rights and limitations.
*/

package pt.webdetails.cdf.dd.model.meta.reader.cdexml.fs;

import org.apache.commons.io.FilenameUtils;
import pt.webdetails.cdf.dd.CdeEngine;
import pt.webdetails.cdf.dd.model.meta.reader.cdexml.XmlAdhocComponentTypeReader;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import pt.webdetails.cdf.dd.model.meta.ComponentType;
import pt.webdetails.cdf.dd.model.meta.MetaModel;
import pt.webdetails.cdf.dd.model.meta.PropertyType;
import pt.webdetails.cdf.dd.model.core.reader.ThingReadException;
import pt.webdetails.cdf.dd.model.meta.CustomComponentType;
import pt.webdetails.cdf.dd.model.meta.PrimitiveComponentType;
import pt.webdetails.cdf.dd.model.meta.WidgetComponentType;
import pt.webdetails.cpf.packager.origin.PluginRepositoryOrigin;
import pt.webdetails.cpf.packager.origin.RepositoryPathOrigin;
import pt.webdetails.cpf.packager.origin.StaticSystemOrigin;
import pt.webdetails.cdf.dd.util.CdeEnvironment;
import pt.webdetails.cdf.dd.util.GenericBasicFileFilter;
import pt.webdetails.cdf.dd.util.Utils;
import pt.webdetails.cpf.packager.origin.PathOrigin;
import pt.webdetails.cpf.repository.api.IBasicFile;
import pt.webdetails.cpf.repository.api.IContentAccessFactory;
import pt.webdetails.cpf.repository.api.IReadAccess;

/**
* Loads XML model files, component types and property types, from the file system, of a Pentaho CDE plugin
* instalation.
*
* @author dcleao
*/
public final class XmlFsPluginModelReader {

  public static final String RESOURCES_DIR = "resources";

  public static final String DEF_BASE_TYPE = PrimitiveComponentType.class.getSimpleName();

  public static final String BASE_DIR = Utils.joinPath( RESOURCES_DIR, "base" );
  public static final String BASE_PROPS_DIR = Utils.joinPath( BASE_DIR, "properties" );
  public static final String BASE_COMPS_DIR = Utils.joinPath( BASE_DIR, "components" );

  public static final String DEF_CUSTOM_TYPE = CustomComponentType.class.getSimpleName();
  public static final String CUSTOM_DIR = Utils.joinPath( RESOURCES_DIR, "custom" );
  public static final String CUSTOM_PROPS_DIR = Utils.joinPath( CUSTOM_DIR, "properties" );

  public static final String DEF_WIDGET_STUB_TYPE = WidgetComponentType.class.getSimpleName();
  public static final String WIDGETS_DIR = "widgets";

  public static final String CUSTOM_PROPS_FILENAME = "property";
  public static final String COMPONENT_FILENAME = "component";
  // extension for component properties definitions
  public static final String DEFINITION_FILE_EXT = "xml";

  protected static final Log logger = LogFactory.getLog( XmlFsPluginModelReader.class );

  // -----------

  private final Boolean continueOnError;

  private IContentAccessFactory contentAccessFactory;

  public XmlFsPluginModelReader( boolean continueOnError ) //the real ctor, only usage calls with false
  {
    this.continueOnError = continueOnError;
  }

  public XmlFsPluginModelReader( IContentAccessFactory caf, boolean continueOnError ) {
    this( false );
    contentAccessFactory = caf;
  }

  /**
   * Reads properties, components and widgets
   *
   * @return model with component and property types
   * @throws ThingReadException
   */
  public MetaModel.Builder read( XmlFsPluginThingReaderFactory factory ) throws ThingReadException {
    MetaModel.Builder model = new MetaModel.Builder();

    // Read Properties
    this.readBaseProperties( model, factory );
    this.readCustomProperties( model, factory );

    // Read Components
    logger.info( String.format( "Loading BASE components from: %s", BASE_COMPS_DIR ) );
    this.readBaseComponents( model, factory );
    this.readCustomComponents( model, factory );
    this.readWidgetStubComponents( model, factory );

    return model;
  }

  private void readBaseComponents( MetaModel.Builder model, XmlFsPluginThingReaderFactory factory )
      throws ThingReadException {
    List<IBasicFile> filesList = CdeEnvironment.getPluginSystemReader( BASE_COMPS_DIR ).listFiles( null,
        new GenericBasicFileFilter( null, DEFINITION_FILE_EXT ), IReadAccess.DEPTH_ALL );
    PathOrigin origin = new StaticSystemOrigin( BASE_COMPS_DIR );
    for ( IBasicFile file : filesList ) {
      this.readComponentsFile( model, factory, file, DEF_BASE_TYPE, origin );
    }
  }

  private void readBaseProperties( MetaModel.Builder model, XmlFsPluginThingReaderFactory factory )
      throws ThingReadException {

    logger.info( String.format( "Loading BASE properties from: %s", BASE_PROPS_DIR ) );

    List<IBasicFile> filesList = CdeEnvironment.getPluginSystemReader( BASE_PROPS_DIR ).listFiles( null,
        new GenericBasicFileFilter( null, DEFINITION_FILE_EXT ), IReadAccess.DEPTH_ALL );

    if ( filesList != null ) {

      for ( IBasicFile file : filesList ) {
        this.readPropertiesFile( model, factory, file );
      }
    }
  }

  private void readCustomProperties( MetaModel.Builder model, XmlFsPluginThingReaderFactory factory )
      throws ThingReadException {

    logger.info( String.format( "Loading CUSTOM properties from: %s", CUSTOM_PROPS_DIR ) );

    List<IBasicFile> filesList = CdeEnvironment.getPluginSystemReader( CUSTOM_PROPS_DIR ).listFiles( null,
        new GenericBasicFileFilter( CUSTOM_PROPS_FILENAME, DEFINITION_FILE_EXT ), IReadAccess.DEPTH_ALL );

    if ( filesList != null ) {
      for ( IBasicFile file : filesList ) {
        this.readPropertiesFile( model, factory, file );
      }
    }
  }

  private void readPropertiesFile( MetaModel.Builder model, XmlFsPluginThingReaderFactory factory, IBasicFile file )
      throws ThingReadException {
    Document doc;
    try {
      doc = Utils.getDocFromFile( file, null );
    } catch ( Exception ex ) {
      ThingReadException ex2 = new ThingReadException( "Cannot read properties file '" + file + "'.", ex );
      if ( !this.continueOnError ) {
        throw ex2;
      }

      // log and move on
      logger.fatal( null, ex2 );
      return;
    }

    // One file can contain multiple definitions.
    @SuppressWarnings( "unchecked" )
    List<Element> propertieElems = doc.selectNodes( "//DesignerProperty" );
    for ( Element propertyElem : propertieElems ) {
      readProperty( model, factory, propertyElem, file );
    }
  }

  private void readProperty(
      MetaModel.Builder modelBuilder,
      XmlFsPluginThingReaderFactory factory,
      Element propertyElem,
      IBasicFile file ) {
    String className = null;
    try {
      className = Utils.getNodeText( "Header/Override", propertyElem );

      if ( StringUtils.isEmpty( className ) ) {
        className = "PropertyType";
      }

      //  String name = Utils.getNodeText("Header/Name", propertyElem); //TODO: do anything with this?

      PropertyType.Builder prop = factory.getPropertyTypeReader().read( propertyElem, file.getPath() );

      modelBuilder.addProperty( prop );

    } catch ( IllegalArgumentException ex ) {
      if ( !this.continueOnError ) {
        throw ex;
      }

      // Just log and move on
      logger.fatal( "Failed to read property ", ex );
    }
  }

  private void readCustomComponents( MetaModel.Builder model, XmlFsPluginThingReaderFactory factory )
      throws ThingReadException {
    for ( PathOrigin origin : CdeEnvironment.getPluginResourceLocationManager().getCustomComponentsLocations() ) {
      readCustomComponentsLocation( model, factory, origin );
    }
  }

  private void readCustomComponentsLocation( MetaModel.Builder model, XmlFsPluginThingReaderFactory factory,
      PathOrigin origin ) throws ThingReadException {
    logger.info( "reading custom components from " + origin );

    GenericBasicFileFilter filter = new GenericBasicFileFilter( COMPONENT_FILENAME, DEFINITION_FILE_EXT );
    IReadAccess access = origin.getReader( contentAccessFactory );
    List<IBasicFile> filesList = access.listFiles( null, filter, IReadAccess.DEPTH_ALL, false, true );

    if ( filesList != null ) {
      logger.debug( String.format( "%d sub-folders found", filesList.size() ) );

      IBasicFile[] filesArray = filesList.toArray( new IBasicFile[] { } );

      Arrays.sort( filesArray, getComponentFileComparator() );

      for ( IBasicFile file : filesList ) {
        this.readComponentsFile( model, factory, file, DEF_CUSTOM_TYPE, origin );
      }
    }
  }

  private void readWidgetStubComponents( MetaModel.Builder model, XmlFsPluginThingReaderFactory factory )
      throws ThingReadException {
    Document doc = null;
    IBasicFile cdeXml = CdeEngine.getInstance().getEnvironment().getCdeXml();
    try {
      if ( cdeXml != null ) {
        doc = Utils.getDocFromFile( cdeXml, null );
      }
    } catch ( Exception e ) {
      String msg = "Cannot read components file 'cde.xml'.";

      if ( !this.continueOnError ) {
        throw new ThingReadException( msg, e );
      }
      // log and move on
      logger.fatal( msg, e );
      return;
    }
    if ( doc != null ) {
      List<Element> widgetLocations = doc.selectNodes( "//widgetsLocations//location" );
      List<List<IBasicFile>> widgetsLists = new ArrayList<List<IBasicFile>>();
      String locations = "";
      for ( Element location : widgetLocations ) {
        try {
          List<IBasicFile> filesList;
          String path = location.getText().toLowerCase().replaceFirst( "/", "" );

          if ( path.startsWith( CdeEnvironment.getSystemDir() + "/" ) ) {
            filesList = CdeEnvironment.getPluginSystemReader().listFiles( location.getText(),
              new GenericBasicFileFilter( COMPONENT_FILENAME, DEFINITION_FILE_EXT ), IReadAccess.DEPTH_ALL, false,
              true );
          } else {
            filesList = CdeEnvironment.getUserContentAccess().listFiles( location.getText(),
              new GenericBasicFileFilter( COMPONENT_FILENAME, DEFINITION_FILE_EXT ), IReadAccess.DEPTH_ALL, false,
              true );
          }

          if ( filesList != null ) {
            widgetsLists.add( filesList );
          }
          locations = locations + location.getText() + ", ";
        } catch ( Exception e ) {
          logger.fatal( "Couldn't load widgets from: " + location.getText(), e );
        }
      }
      logger.info( String.format( "Loading WIDGET components from: %s", locations ) );
      List<String> files = new ArrayList<String>();
      if ( widgetsLists.size() > 0 ) {
        for ( List<IBasicFile> filesList : widgetsLists ) {
          for ( IBasicFile file : filesList ) {
            if ( !files.contains( file.getName() ) ) {
              files.add( file.getName() );
              fixWidgetMeta( file );
              this.readComponentsFile( model, factory, file, DEF_WIDGET_STUB_TYPE,
                  new RepositoryPathOrigin(
                      FilenameUtils.getPath( file.getPath() ) ) );
            } else {
              logger.debug( "Duplicate widget, ignoring " + file.getPath() );
            }
          }
        }
      }
      return;
    }

    logger.info( String.format( "Loading WIDGET components from: %s", WIDGETS_DIR ) );

    List<IBasicFile> filesList = CdeEnvironment.getPluginRepositoryReader( WIDGETS_DIR ).listFiles( null,
        new GenericBasicFileFilter( COMPONENT_FILENAME, DEFINITION_FILE_EXT ), IReadAccess.DEPTH_ALL, false, true );
    PathOrigin widgetsOrigin = new PluginRepositoryOrigin( CdeEngine.getEnv().getPluginRepositoryDir(), WIDGETS_DIR );

    if ( filesList != null ) {
      logger.debug( String.format( "%s widget components found", filesList.size() ) );
      IBasicFile[] filesArray = filesList.toArray( new IBasicFile[] { } );
      Arrays.sort( filesArray, getComponentFileComparator() );
      for ( IBasicFile file : filesList ) {
        this.readComponentsFile( model, factory, file, DEF_WIDGET_STUB_TYPE, widgetsOrigin );
      }
    }

  }

  private void fixWidgetMeta( IBasicFile componentXml ) {
    Document doc = null;

    try {
      if ( CdeEnvironment.getUserContentAccess().fileExists( componentXml.getPath() ) ) {
        doc =
            Utils.getDocFromFile( CdeEnvironment.getUserContentAccess().fetchFile( componentXml.getPath() ), null );
      } else if ( CdeEnvironment.getPluginSystemReader().fileExists( componentXml.getPath() ) ) {
        doc = Utils
            .getDocFromFile( CdeEnvironment.getPluginSystemReader().fetchFile( componentXml.getPath() ), null );
      }
    } catch ( Exception e ) {
      logger.error( "Unable to check meta for " + componentXml.getPath() + ", moving on" );
      return;
    }
    List<Element> wcdfMeta = doc.selectNodes( "//meta[@name='wcdf']" );
    String wcdfName = componentXml.getName().replace( ".component.xml", ".wcdf" );
    String wcdfPath = FilenameUtils.getPath( componentXml.getPath() ) + wcdfName;

    for ( Element meta : wcdfMeta ) {
      String metaText = meta.getText();

      if ( CdeEnvironment.getPluginSystemWriter().fileExists( metaText ) ) {
        wcdfPath = metaText;
      }

      if ( metaText.startsWith( "/" ) && !wcdfPath.startsWith( "/" ) ) {
        wcdfPath = "/" + wcdfPath;
      }

      if ( metaText.equals( wcdfPath ) ) {
        logger.debug( "No need to fix current wcdf meta ( " + metaText + " ) " );
      } else {
        logger.debug( "Fixing wcdf meta, was " + metaText + ", setting " + wcdfPath );
        meta.setText( wcdfPath );
        try {
          if ( CdeEnvironment.getUserContentAccess().fileExists( componentXml.getPath() ) ) {
            CdeEnvironment.getUserContentAccess()
                .saveFile( componentXml.getPath(), new ByteArrayInputStream( doc.asXML().getBytes() ) );
          } else if ( CdeEnvironment.getPluginSystemWriter().fileExists( componentXml.getPath() ) ) {
            CdeEnvironment.getPluginSystemWriter()
                .saveFile( componentXml.getPath(), new ByteArrayInputStream( doc.asXML().getBytes() ) );
          }
        } catch ( Exception e ) {
          logger.error( "Unable to fix meta for " + componentXml.getName() + ", moving on" );
        }
      }
    }
  }


  private Comparator<IBasicFile> getComponentFileComparator() {
    return new Comparator<IBasicFile>() { //TODO: why sort?
      @Override
      public int compare( IBasicFile file1, IBasicFile file2 ) {
        //        if (file1 == null && file2 == null) { //TODO if this makes sense so does one of them being null
        //          return 0;
        //        } else {
        return file1.getFullPath().toLowerCase().compareTo( file2.getFullPath().toLowerCase() );
        //        }
      }
    };
  }


  private void readComponentsFile( MetaModel.Builder model, XmlFsPluginThingReaderFactory factory, IBasicFile file,
      String defaultClassName, PathOrigin origin )
      throws ThingReadException {
    Document doc;
    try {
      doc = Utils.getDocFromFile( file, null );
    } catch ( Exception ex ) {
      String msg = "Cannot read components file '" + file.getFullPath() + "'.";

      if ( !this.continueOnError ) {
        throw new ThingReadException( msg, ex );
      }
      // log and move on
      logger.fatal( msg, ex );
      return;
    }

    // One file can contain multiple definitions.
    @SuppressWarnings( "unchecked" )
    List<Element> componentElems = doc.selectNodes( "//DesignerComponent" );

    if ( logger.isDebugEnabled() && componentElems.size() > 0 ) {
      logger.debug( String.format( "\t%s [%s]", file.getPath(), componentElems.size() ) );
    }

    for ( Element componentElem : componentElems ) {
      readComponent( model, factory, componentElem, file.getPath(), defaultClassName, origin );
    }
  }


  private void readComponent( MetaModel.Builder model, XmlFsPluginThingReaderFactory factory, Element componentElem,
      String sourcePath, String defaultClassName, PathOrigin origin )
      throws ThingReadException {
    String className = Utils.getNodeText( "Header/Override", componentElem );

    if ( StringUtils.isEmpty( className ) ) {
      className = defaultClassName;
    }

    String name = Utils.getNodeText( "Header/Name", componentElem );

    XmlAdhocComponentTypeReader<? extends ComponentType.Builder> reader = factory.getComponentTypeReader( className );
    if ( reader == null ) {
      String msg = "Failed to read component of class '" + className + "' and name " + name;
      if ( !this.continueOnError ) {
        throw new ThingReadException( msg );
      }
      // Just log and move on
      logger.fatal( msg );
    }

    ComponentType.Builder comp = reader.read( componentElem, origin, sourcePath );
    comp.setOrigin( origin );
    model.addComponent( comp );
  }

}
TOP

Related Classes of pt.webdetails.cdf.dd.model.meta.reader.cdexml.fs.XmlFsPluginModelReader

TOP
Copyright © 2015 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.