Package org.hibernate.jpamodelgen.xml

Source Code of org.hibernate.jpamodelgen.xml.JpaDescriptorParser

/*
* JBoss, Home of Professional Open Source
* Copyright 2010, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hibernate.jpamodelgen.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
import javax.xml.validation.Schema;

import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.util.AccessType;
import org.hibernate.jpamodelgen.util.AccessTypeInformation;
import org.hibernate.jpamodelgen.util.FileTimeStampChecker;
import org.hibernate.jpamodelgen.util.StringUtil;
import org.hibernate.jpamodelgen.util.TypeUtils;
import org.hibernate.jpamodelgen.util.xml.XmlParserHelper;
import org.hibernate.jpamodelgen.util.xml.XmlParsingException;
import org.hibernate.jpamodelgen.xml.jaxb.Entity;
import org.hibernate.jpamodelgen.xml.jaxb.EntityMappings;
import org.hibernate.jpamodelgen.xml.jaxb.Persistence;
import org.hibernate.jpamodelgen.xml.jaxb.PersistenceUnitDefaults;
import org.hibernate.jpamodelgen.xml.jaxb.PersistenceUnitMetadata;

/**
* Parser for JPA XML descriptors (persistence.xml and referenced mapping files).
*
* @author Hardy Ferentschik
*/
public class JpaDescriptorParser {
  private static final String DEFAULT_ORM_XML_LOCATION = "/META-INF/orm.xml";
  private static final String SERIALIZATION_FILE_NAME = "Hibernate-Static-Metamodel-Generator.tmp";

  private static final String PERSISTENCE_SCHEMA = "persistence_2_1.xsd";
  private static final String ORM_SCHEMA = "orm_2_1.xsd";

  private final Context context;
  private final List<EntityMappings> entityMappings;
  private final XmlParserHelper xmlParserHelper;

  public JpaDescriptorParser(Context context) {
    this.context = context;
    this.entityMappings = new ArrayList<EntityMappings>();
    this.xmlParserHelper = new XmlParserHelper( context );
  }

  public void parseXml() {
    Collection<String> mappingFileNames = determineMappingFileNames();

    if ( context.doLazyXmlParsing() && mappingFilesUnchanged( mappingFileNames ) ) {
      return;
    }

    loadEntityMappings( mappingFileNames );
    determineDefaultAccessTypeAndMetaCompleteness();
    determineXmlAccessTypes();
    if ( !context.isFullyXmlConfigured() ) {
      // need to take annotations into consideration, since they can override xml settings
      // we have to at least determine whether any of the xml configured entities is influenced by annotations
      determineAnnotationAccessTypes();
    }

    for ( EntityMappings mappings : entityMappings ) {
      String defaultPackageName = mappings.getPackage();
      parseEntities( mappings.getEntity(), defaultPackageName );
      parseEmbeddable( mappings.getEmbeddable(), defaultPackageName );
      parseMappedSuperClass( mappings.getMappedSuperclass(), defaultPackageName );
    }
  }

  private Collection<String> determineMappingFileNames() {
    Collection<String> mappingFileNames = new ArrayList<String>();

    Persistence persistence = getPersistence();
    if ( persistence != null ) {
      // get mapping file names from persistence.xml
      List<Persistence.PersistenceUnit> persistenceUnits = persistence.getPersistenceUnit();
      for ( Persistence.PersistenceUnit unit : persistenceUnits ) {
        mappingFileNames.addAll( unit.getMappingFile() );
      }
    }

    // /META-INF/orm.xml is implicit
    mappingFileNames.add( DEFAULT_ORM_XML_LOCATION );

    // not really part of the official spec, but the processor allows to specify mapping files directly as
    // command line options
    mappingFileNames.addAll( context.getOrmXmlFiles() );
    return mappingFileNames;
  }

  private Persistence getPersistence() {
    Persistence persistence = null;
    String persistenceXmlLocation = context.getPersistenceXmlLocation();
    InputStream stream = xmlParserHelper.getInputStreamForResource( persistenceXmlLocation );
    if ( stream == null ) {
      return null;
    }

    try {
      Schema schema = xmlParserHelper.getSchema( PERSISTENCE_SCHEMA );
      persistence = xmlParserHelper.getJaxbRoot( stream, Persistence.class, schema );
    }
    catch ( XmlParsingException e ) {
      context.logMessage(
          Diagnostic.Kind.WARNING, "Unable to parse persistence.xml: " + e.getMessage()
      );
    }
    return persistence;
  }

  private void loadEntityMappings(Collection<String> mappingFileNames) {
    for ( String mappingFile : mappingFileNames ) {
      InputStream stream = xmlParserHelper.getInputStreamForResource( mappingFile );
      if ( stream == null ) {
        continue;
      }
      EntityMappings mapping = null;
      try {
        Schema schema = xmlParserHelper.getSchema( ORM_SCHEMA );
        mapping = xmlParserHelper.getJaxbRoot( stream, EntityMappings.class, schema );
      }
      catch ( XmlParsingException e ) {
        context.logMessage(
            Diagnostic.Kind.WARNING, "Unable to parse " + mappingFile + ": " + e.getMessage()
        );
      }
      if ( mapping != null ) {
        entityMappings.add( mapping );
      }
    }
  }

  private boolean mappingFilesUnchanged(Collection<String> mappingFileNames) {
    boolean mappingFilesUnchanged = false;
    FileTimeStampChecker fileStampCheck = new FileTimeStampChecker();
    for ( String mappingFile : mappingFileNames ) {
      try {
        URL url = this.getClass().getResource( mappingFile );
        if ( url == null ) {
          continue;
        }
        File file = new File( url.toURI() );
        context.logMessage( Diagnostic.Kind.OTHER, "Check file  " + mappingFile );
        if ( file.exists() ) {
          fileStampCheck.add( mappingFile, file.lastModified() );
        }
      }
      catch ( URISyntaxException e ) {
        // in doubt return false
        return false;
      }
    }

    FileTimeStampChecker serializedTimeStampCheck = loadTimeStampCache();
    if ( serializedTimeStampCheck.equals( fileStampCheck ) ) {
      context.logMessage( Diagnostic.Kind.OTHER, "XML parsing will be skipped due to unchanged xml files" );
      mappingFilesUnchanged = true;
    }
    else {
      saveTimeStampCache( fileStampCheck );
    }

    return mappingFilesUnchanged;
  }

  private void saveTimeStampCache(FileTimeStampChecker fileStampCheck) {
    try {
      File file = getSerializationTmpFile();
      ObjectOutput out = new ObjectOutputStream( new FileOutputStream( file ) );
      out.writeObject( fileStampCheck );
      out.close();
      context.logMessage(
          Diagnostic.Kind.OTHER, "Serialized " + fileStampCheck + " into " + file.getAbsolutePath()
      );
    }
    catch ( IOException e ) {
      // ignore - if the serialization failed we just have to keep parsing the xml
      context.logMessage( Diagnostic.Kind.OTHER, "Error serializing  " + fileStampCheck );
    }
  }

  private File getSerializationTmpFile() {
    File tmpDir = new File( System.getProperty( "java.io.tmpdir" ) );
    return new File( tmpDir, SERIALIZATION_FILE_NAME );
  }

  private FileTimeStampChecker loadTimeStampCache() {
    FileTimeStampChecker serializedTimeStampCheck = new FileTimeStampChecker();
    File file = null;
    try {
      file = getSerializationTmpFile();
      if ( file.exists() ) {
        ObjectInputStream in = new ObjectInputStream( new FileInputStream( file ) );
        serializedTimeStampCheck = (FileTimeStampChecker) in.readObject();
        in.close();
      }
    }
    catch ( Exception e ) {
      // ignore - if the de-serialization failed we just have to keep parsing the xml
      context.logMessage( Diagnostic.Kind.OTHER, "Error de-serializing  " + file );
    }
    return serializedTimeStampCheck;
  }

  private void parseEntities(Collection<Entity> entities, String defaultPackageName) {
    for ( Entity entity : entities ) {
      String fqcn = StringUtil.determineFullyQualifiedClassName( defaultPackageName, entity.getClazz() );

      if ( !xmlMappedTypeExists( fqcn ) ) {
        context.logMessage(
            Diagnostic.Kind.WARNING,
            fqcn + " is mapped in xml, but class does not exist. Skipping meta model generation."
        );
        continue;
      }

      XmlMetaEntity metaEntity = new XmlMetaEntity(
          entity, defaultPackageName, getXmlMappedType( fqcn ), context
      );
      if ( context.containsMetaEntity( fqcn ) ) {
        context.logMessage(
            Diagnostic.Kind.WARNING,
            fqcn + " was already processed once. Skipping second occurrence."
        );
      }
      context.addMetaEntity( fqcn, metaEntity );
    }
  }

  private void parseEmbeddable(Collection<org.hibernate.jpamodelgen.xml.jaxb.Embeddable> embeddables, String defaultPackageName) {
    for ( org.hibernate.jpamodelgen.xml.jaxb.Embeddable embeddable : embeddables ) {
      String fqcn = StringUtil.determineFullyQualifiedClassName( defaultPackageName, embeddable.getClazz() );
      // we have to extract the package name from the fqcn. Maybe the entity was setting a fqcn directly
      String pkg = StringUtil.packageNameFromFqcn( fqcn );

      if ( !xmlMappedTypeExists( fqcn ) ) {
        context.logMessage(
            Diagnostic.Kind.WARNING,
            fqcn + " is mapped in xml, but class does not exist. Skipping meta model generation."
        );
        continue;
      }

      XmlMetaEntity metaEntity = new XmlMetaEntity( embeddable, pkg, getXmlMappedType( fqcn ), context );
      if ( context.containsMetaEmbeddable( fqcn ) ) {
        context.logMessage(
            Diagnostic.Kind.WARNING,
            fqcn + " was already processed once. Skipping second occurrence."
        );
      }
      context.addMetaEmbeddable( fqcn, metaEntity );
    }
  }

  private void parseMappedSuperClass(Collection<org.hibernate.jpamodelgen.xml.jaxb.MappedSuperclass> mappedSuperClasses, String defaultPackageName) {
    for ( org.hibernate.jpamodelgen.xml.jaxb.MappedSuperclass mappedSuperClass : mappedSuperClasses ) {
      String fqcn = StringUtil.determineFullyQualifiedClassName(
          defaultPackageName, mappedSuperClass.getClazz()
      );
      // we have to extract the package name from the fqcn. Maybe the entity was setting a fqcn directly
      String pkg = StringUtil.packageNameFromFqcn( fqcn );

      if ( !xmlMappedTypeExists( fqcn ) ) {
        context.logMessage(
            Diagnostic.Kind.WARNING,
            fqcn + " is mapped in xml, but class does not exist. Skipping meta model generation."
        );
        continue;
      }

      XmlMetaEntity metaEntity = new XmlMetaEntity(
          mappedSuperClass, pkg, getXmlMappedType( fqcn ), context
      );

      if ( context.containsMetaEntity( fqcn ) ) {
        context.logMessage(
            Diagnostic.Kind.WARNING,
            fqcn + " was already processed once. Skipping second occurrence."
        );
      }
      context.addMetaEntity( fqcn, metaEntity );
    }
  }

  private boolean xmlMappedTypeExists(String fullyQualifiedClassName) {
    Elements utils = context.getElementUtils();
    return utils.getTypeElement( fullyQualifiedClassName ) != null;
  }

  private TypeElement getXmlMappedType(String fullyQualifiedClassName) {
    Elements utils = context.getElementUtils();
    return utils.getTypeElement( fullyQualifiedClassName );
  }

  private AccessType determineEntityAccessType(EntityMappings mappings) {
    AccessType accessType = context.getPersistenceUnitDefaultAccessType();
    if ( mappings.getAccess() != null ) {
      accessType = mapXmlAccessTypeToJpaAccessType( mappings.getAccess() );
    }
    return accessType;
  }

  private void determineXmlAccessTypes() {
    for ( EntityMappings mappings : entityMappings ) {
      String fqcn;
      String packageName = mappings.getPackage();
      AccessType defaultAccessType = determineEntityAccessType( mappings );

      for ( Entity entity : mappings.getEntity() ) {
        String name = entity.getClazz();
        fqcn = StringUtil.determineFullyQualifiedClassName( packageName, name );
        AccessType explicitAccessType = null;
        org.hibernate.jpamodelgen.xml.jaxb.AccessType type = entity.getAccess();
        if ( type != null ) {
          explicitAccessType = mapXmlAccessTypeToJpaAccessType( type );
        }
        AccessTypeInformation accessInfo = new AccessTypeInformation(
            fqcn, explicitAccessType, defaultAccessType
        );
        context.addAccessTypeInformation( fqcn, accessInfo );
      }

      for ( org.hibernate.jpamodelgen.xml.jaxb.MappedSuperclass mappedSuperClass : mappings.getMappedSuperclass() ) {
        String name = mappedSuperClass.getClazz();
        fqcn = StringUtil.determineFullyQualifiedClassName( packageName, name );
        AccessType explicitAccessType = null;
        org.hibernate.jpamodelgen.xml.jaxb.AccessType type = mappedSuperClass.getAccess();
        if ( type != null ) {
          explicitAccessType = mapXmlAccessTypeToJpaAccessType( type );
        }
        AccessTypeInformation accessInfo = new AccessTypeInformation(
            fqcn, explicitAccessType, defaultAccessType
        );
        context.addAccessTypeInformation( fqcn, accessInfo );
      }

      for ( org.hibernate.jpamodelgen.xml.jaxb.Embeddable embeddable : mappings.getEmbeddable() ) {
        String name = embeddable.getClazz();
        fqcn = StringUtil.determineFullyQualifiedClassName( packageName, name );
        AccessType explicitAccessType = null;
        org.hibernate.jpamodelgen.xml.jaxb.AccessType type = embeddable.getAccess();
        if ( type != null ) {
          explicitAccessType = mapXmlAccessTypeToJpaAccessType( type );
        }
        AccessTypeInformation accessInfo = new AccessTypeInformation(
            fqcn, explicitAccessType, defaultAccessType
        );
        context.addAccessTypeInformation( fqcn, accessInfo );
      }
    }
  }

  private void determineAnnotationAccessTypes() {
    for ( EntityMappings mappings : entityMappings ) {
      String fqcn;
      String packageName = mappings.getPackage();

      for ( Entity entity : mappings.getEntity() ) {
        String name = entity.getClazz();
        fqcn = StringUtil.determineFullyQualifiedClassName( packageName, name );
        TypeElement element = context.getTypeElementForFullyQualifiedName( fqcn );
        if ( element != null ) {
          TypeUtils.determineAccessTypeForHierarchy( element, context );
        }
      }

      for ( org.hibernate.jpamodelgen.xml.jaxb.MappedSuperclass mappedSuperClass : mappings.getMappedSuperclass() ) {
        String name = mappedSuperClass.getClazz();
        fqcn = StringUtil.determineFullyQualifiedClassName( packageName, name );
        TypeElement element = context.getTypeElementForFullyQualifiedName( fqcn );
        if ( element != null ) {
          TypeUtils.determineAccessTypeForHierarchy( element, context );
        }
      }
    }
  }

  /**
   * Determines the default access type as specified in the <i>persistence-unit-defaults</i> as well as whether the
   * xml configuration is complete and annotations should be ignored.
   * <p/>
   * Note, the spec says:
   * <ul>
   * <li>The persistence-unit-metadata element contains metadata for the entire persistence unit. It is
   * undefined if this element occurs in multiple mapping files within the same persistence unit.</li>
   * <li>If the xml-mapping-metadata-complete subelement is specified, the complete set of mapping
   * metadata for the persistence unit is contained in the XML mapping files for the persistence unit, and any
   * persistence annotations on the classes are ignored.</li>
   * <li>When the xml-mapping-metadata-complete element is specified, any metadata-complete attributes specified
   * within the entity, mapped-superclass, and embeddable elements are ignored.<li>
   * </ul>
   */
  private void determineDefaultAccessTypeAndMetaCompleteness() {
    for ( EntityMappings mappings : entityMappings ) {
      PersistenceUnitMetadata meta = mappings.getPersistenceUnitMetadata();
      if ( meta != null ) {
        if ( meta.getXmlMappingMetadataComplete() != null ) {
          context.mappingDocumentFullyXmlConfigured( true );
        }
        else {
          context.mappingDocumentFullyXmlConfigured( false );
        }

        PersistenceUnitDefaults persistenceUnitDefaults = meta.getPersistenceUnitDefaults();
        if ( persistenceUnitDefaults != null ) {
          org.hibernate.jpamodelgen.xml.jaxb.AccessType xmlAccessType = persistenceUnitDefaults.getAccess();
          if ( xmlAccessType != null ) {
            context.setPersistenceUnitDefaultAccessType( mapXmlAccessTypeToJpaAccessType( xmlAccessType ) );
          }
        }
      }
      else {
        context.mappingDocumentFullyXmlConfigured( false );
      }
    }
  }

  private AccessType mapXmlAccessTypeToJpaAccessType(org.hibernate.jpamodelgen.xml.jaxb.AccessType xmlAccessType) {
    switch ( xmlAccessType ) {
      case FIELD: {
        return AccessType.FIELD;
      }
      case PROPERTY: {
        return AccessType.PROPERTY;
      }
      default: {
      }
    }
    return null;
  }
}

TOP

Related Classes of org.hibernate.jpamodelgen.xml.JpaDescriptorParser

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.