| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.jaxb.compiler; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.lang.reflect.Type; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import javax.xml.XMLConstants; |
| import jakarta.xml.bind.SchemaOutputResolver; |
| import javax.xml.namespace.QName; |
| |
| import org.eclipse.persistence.core.sessions.CoreProject; |
| import org.eclipse.persistence.internal.oxm.Constants; |
| import org.eclipse.persistence.internal.oxm.mappings.Descriptor; |
| import org.eclipse.persistence.internal.oxm.schema.SchemaModelProject; |
| import org.eclipse.persistence.internal.oxm.schema.model.Schema; |
| import org.eclipse.persistence.internal.oxm.schema.model.SchemaCompareByNamespace; |
| import org.eclipse.persistence.jaxb.TypeMappingInfo; |
| import org.eclipse.persistence.jaxb.javamodel.Helper; |
| import org.eclipse.persistence.jaxb.javamodel.JavaClass; |
| import org.eclipse.persistence.jaxb.javamodel.JavaModelInput; |
| import org.eclipse.persistence.jaxb.javamodel.reflection.JavaModelInputImpl; |
| import org.eclipse.persistence.jaxb.xmlmodel.XmlBindings; |
| import org.eclipse.persistence.logging.AbstractSessionLog; |
| import org.eclipse.persistence.logging.SessionLog; |
| import org.eclipse.persistence.oxm.NamespaceResolver; |
| import org.eclipse.persistence.oxm.XMLContext; |
| import org.eclipse.persistence.oxm.XMLMarshaller; |
| import org.eclipse.persistence.sessions.Project; |
| |
| /** |
| * INTERNAL: |
| * <p><b>Purpose:</b>The purpose of this class is to act as an entry point into the |
| * TopLink JAXB 2.0 Generation framework |
| * <p><b>Responsibilities:</b><ul> |
| * <li>Run initial processing on a list of classes to create TypeInfo meta data</li> |
| * <li>Provide API to generate Schema Files</li> |
| * <li>Provide API to generate a TopLink Project</li> |
| * <li>Act as an integration point with WebServices</li> |
| * </ul> |
| * <p> This class acts as an entry point into JAXB 2.0 Generation. A Generator is created with a |
| * specific set of JAXB 2.0 Annotated classes and then performs actions on those, such as |
| * generating schema files, or generating TopLink Projects. Additional information is returned |
| * from the schema generation methods as a means of integration with WebServices. |
| * |
| * @author mmacivor |
| * @since Oracle TopLink 11.1.1.0.0 |
| * @see AnnotationsProcessor |
| * @see MappingsGenerator |
| * @see SchemaGenerator |
| */ |
| public class Generator { |
| |
| private AnnotationsProcessor annotationsProcessor; |
| private SchemaGenerator schemaGenerator; |
| private MappingsGenerator mappingsGenerator; |
| private Helper helper; |
| private Map<Type, TypeMappingInfo> typeToTypeMappingInfo; |
| |
| /** |
| * This constructor creates a Helper using the JavaModelInput |
| * instance's JavaModel. Annotations are processed here as well. |
| * |
| */ |
| public Generator(JavaModelInput jModelInput) { |
| helper = new Helper(jModelInput.getJavaModel()); |
| configureFacetsGeneration(jModelInput); |
| annotationsProcessor = new AnnotationsProcessor(helper); |
| schemaGenerator = new SchemaGenerator(helper); |
| mappingsGenerator = new MappingsGenerator(helper); |
| annotationsProcessor.processClassesAndProperties(jModelInput.getJavaClasses(), null); |
| } |
| |
| /** |
| * This constructor will process and apply the given XmlBindings as appropriate. Classes |
| * declared in the bindings will be amalgamated with any classes in the JavaModelInput. |
| * |
| * If xmlBindings is null or empty, AnnotationsProcessor will be used to process |
| * annotations as per usual. |
| * |
| * @param xmlBindings map of XmlBindings keyed on package name |
| */ |
| public Generator(JavaModelInput jModelInput, Map<String, XmlBindings> xmlBindings, ClassLoader cLoader, String defaultTargetNamespace, boolean enableXmlAccessorFactory) { |
| helper = new Helper(jModelInput.getJavaModel()); |
| configureFacetsGeneration(jModelInput); |
| annotationsProcessor = new AnnotationsProcessor(helper); |
| annotationsProcessor.setXmlAccessorFactorySupport(enableXmlAccessorFactory); |
| annotationsProcessor.setDefaultTargetNamespace(defaultTargetNamespace); |
| schemaGenerator = new SchemaGenerator(helper); |
| mappingsGenerator = new MappingsGenerator(helper); |
| if (xmlBindings != null && !xmlBindings.isEmpty()) { |
| new XMLProcessor(xmlBindings).processXML(annotationsProcessor, jModelInput, null, null); |
| } else { |
| annotationsProcessor.processClassesAndProperties(jModelInput.getJavaClasses(), null); |
| } |
| } |
| |
| /** |
| * This constructor creates a Helper using the JavaModelInput |
| * instance's JavaModel and a map of javaclasses that were generated from Type objects. |
| * Annotations are processed here as well. |
| * |
| */ |
| public Generator(JavaModelInput jModelInput, TypeMappingInfo[] typeMappingInfos, JavaClass[] javaClasses, Map<Type, TypeMappingInfo> typeToTypeMappingInfo, String defaultTargetNamespace) { |
| helper = new Helper(jModelInput.getJavaModel()); |
| configureFacetsGeneration(jModelInput); |
| annotationsProcessor = new AnnotationsProcessor(helper); |
| annotationsProcessor.setDefaultTargetNamespace(defaultTargetNamespace); |
| schemaGenerator = new SchemaGenerator(helper); |
| mappingsGenerator = new MappingsGenerator(helper); |
| this.typeToTypeMappingInfo = typeToTypeMappingInfo; |
| annotationsProcessor.processClassesAndProperties(javaClasses, typeMappingInfos); |
| } |
| |
| /** |
| * This constructor will process and apply the given XmlBindings as appropriate. Classes |
| * declared in the bindings will be amalgamated with any classes in the JavaModelInput. |
| * |
| * If xmlBindings is null or empty, AnnotationsProcessor will be used to process |
| * annotations as per usual. |
| * |
| * @param xmlBindings map of XmlBindings keyed on package name |
| */ |
| public Generator(JavaModelInput jModelInput, TypeMappingInfo[] typeMappingInfos, JavaClass[] javaClasses, Map<Type, TypeMappingInfo> typeToTypeMappingInfo, Map<String, XmlBindings> xmlBindings, ClassLoader cLoader, String defaultTargetNamespace, boolean enableXmlAccessorFactory) { |
| helper = new Helper(jModelInput.getJavaModel()); |
| configureFacetsGeneration(jModelInput); |
| annotationsProcessor = new AnnotationsProcessor(helper); |
| annotationsProcessor.setXmlAccessorFactorySupport(enableXmlAccessorFactory); |
| annotationsProcessor.setDefaultTargetNamespace(defaultTargetNamespace); |
| schemaGenerator = new SchemaGenerator(helper); |
| mappingsGenerator = new MappingsGenerator(helper); |
| this.typeToTypeMappingInfo = typeToTypeMappingInfo; |
| if (xmlBindings != null && !xmlBindings.isEmpty()) { |
| new XMLProcessor(xmlBindings).processXML(annotationsProcessor, jModelInput, typeMappingInfos, javaClasses); |
| } else { |
| annotationsProcessor.processClassesAndProperties(javaClasses, typeMappingInfos); |
| } |
| } |
| |
| /** |
| * This event is called when mappings generation is completed, |
| * and provides a chance to deference anything that is no longer |
| * needed (to reduce the memory footprint of this object). |
| */ |
| public void postInitialize() { |
| mappingsGenerator = null; |
| annotationsProcessor.postInitialize(); |
| schemaGenerator = null; |
| } |
| |
| /** |
| * |
| */ |
| public boolean hasMarshalCallbacks() { |
| return getMarshalCallbacks()!=null && !getMarshalCallbacks().isEmpty(); |
| } |
| |
| public boolean hasUnmarshalCallbacks() { |
| return getUnmarshalCallbacks()!=null && !getUnmarshalCallbacks().isEmpty(); |
| } |
| |
| public CoreProject generateProject() throws Exception { |
| mappingsGenerator.getClassToGeneratedClasses().putAll(annotationsProcessor.getArrayClassesToGeneratedClasses()); |
| CoreProject p = mappingsGenerator.generateProject(annotationsProcessor.getTypeInfoClasses(), annotationsProcessor.getTypeInfos(), annotationsProcessor.getUserDefinedSchemaTypes(), annotationsProcessor.getPackageToPackageInfoMappings(), annotationsProcessor.getGlobalElements(), annotationsProcessor.getLocalElements(), annotationsProcessor.getTypeMappingInfosToGeneratedClasses(), annotationsProcessor.getTypeMappingInfoToAdapterClasses(),annotationsProcessor.isDefaultNamespaceAllowed()); |
| annotationsProcessor.getArrayClassesToGeneratedClasses().putAll(mappingsGenerator.getClassToGeneratedClasses()); |
| return p; |
| } |
| |
| public java.util.Collection<Schema> generateSchema() { |
| schemaGenerator.generateSchema(annotationsProcessor.getTypeInfoClasses(), annotationsProcessor.getTypeInfos(), annotationsProcessor.getUserDefinedSchemaTypes(), annotationsProcessor.getPackageToPackageInfoMappings(), null, annotationsProcessor.getArrayClassesToGeneratedClasses()); |
| return schemaGenerator.getAllSchemas(); |
| } |
| |
| public Map<String, SchemaTypeInfo> generateSchemaFiles(String schemaPath, Map<QName, Type> additionalGlobalElements) throws FileNotFoundException { |
| // process any additional global elements |
| processAdditionalElements(additionalGlobalElements, annotationsProcessor); |
| |
| schemaGenerator.generateSchema(annotationsProcessor.getTypeInfoClasses(), annotationsProcessor.getTypeInfos(), annotationsProcessor.getUserDefinedSchemaTypes(), annotationsProcessor.getPackageToPackageInfoMappings(), annotationsProcessor.getGlobalElements(), annotationsProcessor.getArrayClassesToGeneratedClasses()); |
| Project proj = new SchemaModelProject(); |
| XMLContext context = new XMLContext(proj); |
| XMLMarshaller marshaller = context.createMarshaller(); |
| Descriptor schemaDescriptor = (Descriptor)proj.getDescriptor(Schema.class); |
| |
| java.util.Collection<Schema> schemas = schemaGenerator.getAllSchemas(); |
| for(Schema schema : schemas) { |
| File file = new File(schemaPath + "/" + schema.getName()); |
| NamespaceResolver schemaNamespaces = schema.getNamespaceResolver(); |
| schemaNamespaces.put(Constants.SCHEMA_PREFIX, XMLConstants.W3C_XML_SCHEMA_NS_URI); |
| schemaDescriptor.setNamespaceResolver(schemaNamespaces); |
| marshaller.marshal(schema, new FileOutputStream(file)); |
| } |
| return schemaGenerator.getSchemaTypeInfo(); |
| } |
| |
| public Map<String, SchemaTypeInfo> generateSchemaFiles(SchemaOutputResolver outputResolver, Map<QName, Type> additionalGlobalElements) { |
| // process any additional global elements |
| processAdditionalElements(additionalGlobalElements, annotationsProcessor); |
| |
| schemaGenerator.generateSchema(annotationsProcessor.getTypeInfoClasses(), annotationsProcessor.getTypeInfos(), annotationsProcessor.getUserDefinedSchemaTypes(), annotationsProcessor.getPackageToPackageInfoMappings(), annotationsProcessor.getGlobalElements(), annotationsProcessor.getArrayClassesToGeneratedClasses(), outputResolver); |
| Project proj = new SchemaModelProject(); |
| XMLContext context = new XMLContext(proj); |
| XMLMarshaller marshaller = context.createMarshaller(); |
| |
| Descriptor schemaDescriptor = (Descriptor)proj.getDescriptor(Schema.class); |
| |
| java.util.Collection<Schema> schemas = schemaGenerator.getAllSchemas(); |
| // make sure that schemas will be passed to the output in specified order |
| if (schemas instanceof List) { |
| ((List<Schema>)schemas).sort(new SchemaCompareByNamespace()); |
| } |
| for(Schema schema : schemas) { |
| try { |
| NamespaceResolver schemaNamespaces = schema.getNamespaceResolver(); |
| schemaNamespaces.put(Constants.SCHEMA_PREFIX, XMLConstants.W3C_XML_SCHEMA_NS_URI); |
| schemaDescriptor.setNamespaceResolver(schemaNamespaces); |
| // make sure we don't call into the provided output resolver more than once |
| javax.xml.transform.Result target; |
| if (schema.hasResult()) { |
| target = schema.getResult(); |
| } else { |
| target = outputResolver.createOutput(schema.getTargetNamespace(), schema.getName()); |
| } |
| marshaller.marshal(schema, target); |
| } catch (IOException ex) { |
| ex.printStackTrace(); |
| } |
| } |
| return schemaGenerator.getSchemaTypeInfo(); |
| } |
| |
| /** |
| * Convenience method that processes a given map of QName-Type entries. For each an ElementDeclaration |
| * is created and added to the given AnnotationsProcessor instance's map of global elements. |
| * |
| * It is assumed that the map of QName-Type entries contains Type instances that are either a Class or |
| * a ParameterizedType. |
| * |
| */ |
| private void processAdditionalElements(Map<QName, Type> additionalGlobalElements, AnnotationsProcessor annotationsProcessor) { |
| if (additionalGlobalElements != null) { |
| ElementDeclaration declaration; |
| for(Entry<QName, Type> entry : additionalGlobalElements.entrySet()) { |
| QName key = entry.getKey(); |
| Type type = entry.getValue(); |
| TypeMappingInfo tmi = null; |
| if(this.typeToTypeMappingInfo != null) { |
| tmi = this.typeToTypeMappingInfo.get(type); |
| } |
| |
| if(tmi != null) { |
| if(annotationsProcessor.getTypeMappingInfosToGeneratedClasses().get(tmi) != null) { |
| type = annotationsProcessor.getTypeMappingInfosToGeneratedClasses().get(tmi); |
| } |
| } |
| JavaClass jClass = null; |
| if (type instanceof Class) { |
| Class<?> tClass = (Class<?>) type; |
| jClass = helper.getJavaClass(tClass); |
| } |
| // if no type is available don't do anything |
| if (jClass != null) { |
| declaration = new ElementDeclaration(key, jClass, jClass.getQualifiedName(), false); |
| annotationsProcessor.getGlobalElements().put(key, declaration); |
| } |
| } |
| } |
| } |
| |
| public Map<String, UnmarshalCallback> getUnmarshalCallbacks() { |
| return annotationsProcessor.getUnmarshalCallbacks(); |
| } |
| |
| public Map<String, MarshalCallback> getMarshalCallbacks() { |
| return annotationsProcessor.getMarshalCallbacks(); |
| } |
| |
| public MappingsGenerator getMappingsGenerator() { |
| return this.mappingsGenerator; |
| } |
| |
| public AnnotationsProcessor getAnnotationsProcessor() { |
| return annotationsProcessor; |
| } |
| |
| public void setTypeToTypeMappingInfo(Map<Type, TypeMappingInfo> typesToTypeMapping) { |
| this.typeToTypeMappingInfo = typesToTypeMapping; |
| } |
| |
| public Map<Type, TypeMappingInfo> getTypeToTypeMappingInfo() { |
| return this.typeToTypeMappingInfo; |
| } |
| |
| private void configureFacetsGeneration(JavaModelInput jModelInput) { |
| if (jModelInput instanceof JavaModelInputImpl) { |
| helper.setFacets(((JavaModelInputImpl) jModelInput).isFacets()); |
| } else { |
| String msg = "MOXy BV: Facets generation could not be configured. EclipseLink''s JavaModelInputImpl was " + |
| "not detected, instead JavaModelInput is of class: {0}"; |
| AbstractSessionLog.getLog().log(SessionLog.WARNING, SessionLog.MOXY, msg, new Object[] {jModelInput.getClass()}, false); |
| } |
| } |
| |
| } |