From 2b8ab9942917e501fe92f34b02a9b897591af42f Mon Sep 17 00:00:00 2001 From: Koen Aers Date: Wed, 29 Oct 2025 14:52:38 +0100 Subject: [PATCH 1/2] HHH-19879: Move Hibernate Tools' reveng module to Hibernate ORM and merge the relevant ant/gradle/maven plugins - Add the Reverse Engineering module --- settings.gradle | 3 + .../hibernate-reveng/hibernate-reveng.gradle | 14 + .../tool/reveng/api/core/AssociationInfo.java | 27 + .../tool/reveng/api/core/RevengDialect.java | 126 +++ .../reveng/api/core/RevengDialectFactory.java | 112 ++ .../tool/reveng/api/core/RevengSettings.java | 110 ++ .../tool/reveng/api/core/RevengStrategy.java | 211 ++++ .../api/core/RevengStrategyFactory.java | 111 ++ .../tool/reveng/api/core/TableIdentifier.java | 95 ++ .../reveng/api/export/ArtifactCollector.java | 38 + .../tool/reveng/api/export/Exporter.java | 36 + .../reveng/api/export/ExporterConstants.java | 40 + .../reveng/api/export/ExporterFactory.java | 43 + .../tool/reveng/api/export/ExporterType.java | 42 + .../DefaultJavaPrettyPrinterStrategy.java | 41 + .../api/metadata/MetadataConstants.java | 24 + .../api/metadata/MetadataDescriptor.java | 30 + .../metadata/MetadataDescriptorFactory.java | 52 + .../reveng/api/stat/StatisticsBrowser.java | 128 +++ .../tool/reveng/api/version/Version.java | 9 + .../tool/reveng/api/xml/XMLPrettyPrinter.java | 64 ++ .../api/xml/XMLPrettyPrinterStrategy.java | 23 + .../ide/completion/AntlrSimpleHQLLexer.java | 56 + .../ide/completion/CompletionHelper.java | 88 ++ .../completion/ConfigurationCompletion.java | 364 +++++++ .../ide/completion/EntityNameReference.java | 60 ++ .../reveng/ide/completion/HQLAnalyzer.java | 262 +++++ .../reveng/ide/completion/HQLCodeAssist.java | 129 +++ .../ide/completion/HQLCompletionProposal.java | 217 ++++ .../reveng/ide/completion/IHQLCodeAssist.java | 36 + .../completion/IHQLCompletionRequestor.java | 33 + .../reveng/ide/completion/SimpleHQLLexer.java | 34 + .../ide/completion/SimpleLexerException.java | 49 + .../tool/reveng/ide/completion/SubQuery.java | 169 +++ .../internal/core/RevengMetadataBuilder.java | 154 +++ .../core/RevengMetadataCollector.java | 116 +++ .../internal/core/binder/AbstractBinder.java | 58 ++ .../core/binder/BasicPropertyBinder.java | 49 + .../core/binder/BasicValueBinder.java | 52 + .../internal/core/binder/BinderContext.java | 56 + .../internal/core/binder/BinderUtils.java | 108 ++ .../binder/CollectionBinderSecondPass.java | 106 ++ .../core/binder/CollectionPropertyBinder.java | 70 ++ .../core/binder/EntityPropertyBinder.java | 70 ++ .../core/binder/ForeignKeyBinder.java | 146 +++ .../internal/core/binder/ForeignKeyUtils.java | 91 ++ .../internal/core/binder/ManyToOneBinder.java | 73 ++ .../internal/core/binder/OneToManyBinder.java | 217 ++++ .../internal/core/binder/OneToOneBinder.java | 108 ++ .../core/binder/PrimaryKeyBinder.java | 339 ++++++ .../internal/core/binder/PrimaryKeyInfo.java | 27 + .../internal/core/binder/PropertyBinder.java | 101 ++ .../internal/core/binder/RootClassBinder.java | 215 ++++ .../internal/core/binder/TypeUtils.java | 131 +++ .../core/binder/VersionPropertyBinder.java | 107 ++ .../core/dialect/AbstractMetaDataDialect.java | 201 ++++ .../core/dialect/CachedMetaDataDialect.java | 224 ++++ .../core/dialect/H2MetaDataDialect.java | 154 +++ .../core/dialect/HSQLMetaDataDialect.java | 116 +++ .../core/dialect/JDBCMetaDataDialect.java | 224 ++++ .../core/dialect/MySQLMetaDataDialect.java | 99 ++ .../core/dialect/OracleMetaDataDialect.java | 743 +++++++++++++ .../core/dialect/ResultSetIterator.java | 111 ++ .../dialect/SQLServerMetaDataDialect.java | 95 ++ .../core/reader/BasicColumnProcessor.java | 161 +++ .../internal/core/reader/DatabaseReader.java | 172 +++ .../core/reader/ForeignKeyProcessor.java | 369 +++++++ .../internal/core/reader/ForeignKeysInfo.java | 80 ++ .../internal/core/reader/IndexProcessor.java | 184 ++++ .../core/reader/PrimaryKeyProcessor.java | 183 ++++ .../internal/core/reader/TableCollector.java | 161 +++ .../core/strategy/AbstractStrategy.java | 354 +++++++ .../core/strategy/DefaultStrategy.java | 24 + .../core/strategy/DelegatingStrategy.java | 175 ++++ .../core/strategy/MetaAttributeHelper.java | 148 +++ .../core/strategy/OverrideBinder.java | 479 +++++++++ .../core/strategy/OverrideRepository.java | 814 +++++++++++++++ .../core/strategy/SQLTypeMapping.java | 167 +++ .../internal/core/strategy/TableFilter.java | 176 ++++ .../core/strategy/TableSelectorStrategy.java | 45 + .../core/util/EnhancedBasicValue.java | 57 + .../internal/core/util/EnhancedComponent.java | 65 ++ .../internal/core/util/EnhancedValue.java | 34 + .../internal/core/util/RevengUtils.java | 112 ++ .../internal/export/cfg/CfgExporter.java | 203 ++++ .../export/common/AbstractExporter.java | 244 +++++ .../export/common/ConfigurationNavigator.java | 87 ++ .../common/DefaultArtifactCollector.java | 114 ++ .../export/common/DefaultValueVisitor.java | 141 +++ .../common/EntityNameFromValueVisitor.java | 103 ++ .../export/common/ExporterSettings.java | 46 + .../export/common/GenericExporter.java | 185 ++++ .../export/common/TemplateHelper.java | 254 +++++ .../export/common/TemplateProducer.java | 117 +++ .../internal/export/dao/DaoExporter.java | 64 ++ .../reveng/internal/export/dao/DaoHelper.java | 40 + .../internal/export/ddl/DdlExporter.java | 168 +++ .../internal/export/doc/DocExporter.java | 656 ++++++++++++ .../reveng/internal/export/doc/DocFile.java | 182 ++++ .../internal/export/doc/DocFileManager.java | 512 +++++++++ .../reveng/internal/export/doc/DocFolder.java | 151 +++ .../reveng/internal/export/doc/DocHelper.java | 508 +++++++++ .../internal/export/hbm/Cfg2HbmTool.java | 478 +++++++++ .../hbm/HBMTagForPersistentClassVisitor.java | 61 ++ .../export/hbm/HBMTagForValueVisitor.java | 104 ++ .../internal/export/hbm/HbmExporter.java | 89 ++ .../hbm/HibernateMappingGlobalSettings.java | 163 +++ .../internal/export/java/BasicPOJOClass.java | 977 ++++++++++++++++++ .../internal/export/java/Cfg2JavaTool.java | 416 ++++++++ .../export/java/ComponentPOJOClass.java | 190 ++++ .../internal/export/java/EntityPOJOClass.java | 917 ++++++++++++++++ .../internal/export/java/ImportContext.java | 39 + .../export/java/ImportContextImpl.java | 155 +++ .../internal/export/java/JavaExporter.java | 52 + .../export/java/JavaTypeFromValueVisitor.java | 118 +++ .../export/java/MetaAttributeConstants.java | 30 + .../export/java/MetaAttributeHelper.java | 87 ++ .../export/java/NoopImportContext.java | 34 + .../internal/export/java/POJOClass.java | 125 +++ .../export/lint/BadCachingDetector.java | 52 + .../reveng/internal/export/lint/Detector.java | 37 + .../export/lint/EntityModelDetector.java | 46 + .../reveng/internal/export/lint/HbmLint.java | 66 ++ .../internal/export/lint/HbmLintExporter.java | 43 + .../export/lint/InstrumentationDetector.java | 93 ++ .../reveng/internal/export/lint/Issue.java | 48 + .../internal/export/lint/IssueCollector.java | 24 + .../export/lint/RelationalModelDetector.java | 50 + .../export/lint/SchemaByMetaDataDetector.java | 336 ++++++ .../export/lint/SequenceCollector.java | 77 ++ .../lint/ShadowedIdentifierDetector.java | 36 + .../internal/export/query/QueryExporter.java | 105 ++ .../metadata/JpaMetadataDescriptor.java | 77 ++ .../metadata/NativeMetadataDescriptor.java | 71 ++ .../metadata/RevengMetadataDescriptor.java | 64 ++ .../internal/stat/StatisticsCellRenderer.java | 71 ++ .../internal/stat/StatisticsTreeModel.java | 115 +++ .../internal/util/AbstractTreeModel.java | 175 ++++ .../internal/util/AnnotationBuilder.java | 160 +++ .../reveng/internal/util/BeanTableModel.java | 132 +++ .../internal/util/IteratorTransformer.java | 44 + .../util/JdbcToHibernateTypeHelper.java | 224 ++++ .../reveng/internal/util/NameConverter.java | 163 +++ .../reveng/internal/util/ReflectionUtil.java | 33 + .../util/SkipBackRefPropertyIterator.java | 77 ++ .../tool/reveng/internal/util/StringUtil.java | 83 ++ .../internal/util/TableNameQualifier.java | 33 + .../tool/reveng/internal/util/ValueUtil.java | 49 + .../xml/AbstractXMLPrettyPrinterStrategy.java | 80 ++ .../xml/DOM3LSPrettyPrinterStrategy.java | 81 ++ .../xml/TrAXPrettyPrinterStrategy.java | 94 ++ .../xml/XMLPrettyPrinterStrategyFactory.java | 52 + .../src/main/resources/dao/daohome.ftl | 279 +++++ .../src/main/resources/debug.ftl | 34 + .../src/main/resources/doc/common.ftl | 50 + .../src/main/resources/doc/doc-style.css | 213 ++++ .../resources/doc/entities/allEntity-list.ftl | 36 + .../main/resources/doc/entities/entity.ftl | 436 ++++++++ .../src/main/resources/doc/entities/index.ftl | 38 + .../resources/doc/entities/package-list.ftl | 39 + .../doc/entities/package-summary.ftl | 58 ++ .../doc/entities/perPackageEntity-list.ftl | 39 + .../main/resources/doc/entities/summary.ftl | 60 ++ .../src/main/resources/doc/hibernate_logo.gif | Bin 0 -> 2515 bytes .../src/main/resources/doc/index.html | 25 + .../src/main/resources/doc/inherit.gif | Bin 0 -> 71 bytes .../src/main/resources/doc/tables/index.ftl | 38 + .../main/resources/doc/tables/schema-list.ftl | 40 + .../resources/doc/tables/schema-summary.ftl | 53 + .../doc/tables/schema-table-list.ftl | 36 + .../src/main/resources/doc/tables/summary.ftl | 62 ++ .../main/resources/doc/tables/table-list.ftl | 36 + .../src/main/resources/doc/tables/table.ftl | 308 ++++++ .../main/resources/dot/entitygraph.dot.ftl | 90 ++ .../src/main/resources/dot/tablegraph.dot.ftl | 72 ++ .../src/main/resources/hbm/any.hbm.ftl | 37 + .../src/main/resources/hbm/array.hbm.ftl | 41 + .../src/main/resources/hbm/bag.hbm.ftl | 36 + .../hbm/collection-tableattr.hbm.ftl | 22 + .../src/main/resources/hbm/column.hbm.ftl | 23 + .../src/main/resources/hbm/component.hbm.ftl | 28 + .../resources/hbm/composite-element.hbm.ftl | 27 + .../resources/hbm/dynamic-component.hbm.ftl | 28 + .../resources/hbm/element-element.hbm.ftl | 20 + .../src/main/resources/hbm/filter-def.hbm.ftl | 23 + .../src/main/resources/hbm/generalhbm.hbm.ftl | 41 + .../resources/hbm/hibernate-mapping.hbm.ftl | 51 + .../src/main/resources/hbm/id.hbm.ftl | 89 ++ .../src/main/resources/hbm/idbag.hbm.ftl | 44 + .../src/main/resources/hbm/import.hbm.ftl | 20 + .../src/main/resources/hbm/key.hbm.ftl | 23 + .../src/main/resources/hbm/list.hbm.ftl | 43 + .../resources/hbm/many-to-any-element.hbm.ftl | 26 + .../hbm/many-to-many-element.hbm.ftl | 23 + .../main/resources/hbm/many-to-one.hbm.ftl | 48 + .../src/main/resources/hbm/map.hbm.ftl | 49 + .../src/main/resources/hbm/meta.hbm.ftl | 22 + .../hbm/nested-composite-element.hbm.ftl | 27 + .../resources/hbm/one-to-many-element.hbm.ftl | 19 + .../src/main/resources/hbm/one-to-one.hbm.ftl | 39 + .../resources/hbm/persistentclass.hbm.ftl | 161 +++ .../src/main/resources/hbm/pkcolumn.hbm.ftl | 16 + .../resources/hbm/primitive-array.hbm.ftl | 41 + .../src/main/resources/hbm/properties.hbm.ftl | 24 + .../src/main/resources/hbm/property.hbm.ftl | 58 ++ .../src/main/resources/hbm/query.hbm.ftl | 36 + .../src/main/resources/hbm/set.hbm.ftl | 37 + .../src/main/resources/hbm/sql-query.hbm.ftl | 60 ++ .../src/main/resources/hbm/timestamp.hbm.ftl | 22 + .../src/main/resources/hbm/version.hbm.ftl | 25 + .../src/main/resources/lint/text-report.ftl | 18 + .../pojo/Ejb3PropertyGetAnnotation.ftl | 37 + .../resources/pojo/Ejb3TypeDeclaration.ftl | 32 + .../resources/pojo/GetPropertyAnnotation.ftl | 16 + .../src/main/resources/pojo/Pojo.ftl | 44 + .../main/resources/pojo/PojoConstructors.ftl | 41 + .../resources/pojo/PojoEqualsHashcode.ftl | 33 + .../resources/pojo/PojoExtraClassCode.ftl | 19 + .../src/main/resources/pojo/PojoFields.ftl | 23 + .../pojo/PojoInterfacePropertyAccessors.ftl | 23 + .../resources/pojo/PojoPropertyAccessors.ftl | 33 + .../src/main/resources/pojo/PojoToString.ftl | 29 + .../resources/pojo/PojoTypeDeclaration.ftl | 20 + .../src/main/resources/pojo/test.ftl | 20 + ...ReverseEngineeringStrategyFactoryTest.java | 53 + .../api/export/ExporterFactoryTest.java | 53 + .../reveng/api/export/ExporterTypeTest.java | 36 + .../tool/reveng/api/version/VersionTest.java | 14 + .../reveng/api/xml/XMLPrettyPrinterTest.java | 79 ++ .../internal/core/binder/TypeUtilsTest.java | 22 + .../strategy/MetaAttributeHelperTest.java | 57 + .../internal/util/ReflectionUtilTest.java | 42 + .../reveng/internal/util/StringUtilTest.java | 34 + 233 files changed, 26047 insertions(+) create mode 100644 tooling/hibernate-reveng/hibernate-reveng.gradle create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/AssociationInfo.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengDialect.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengDialectFactory.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengSettings.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengStrategy.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengStrategyFactory.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/TableIdentifier.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ArtifactCollector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/Exporter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterConstants.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterFactory.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterType.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/java/DefaultJavaPrettyPrinterStrategy.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataConstants.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataDescriptor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataDescriptorFactory.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/stat/StatisticsBrowser.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/version/Version.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinterStrategy.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/AntlrSimpleHQLLexer.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/CompletionHelper.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/ConfigurationCompletion.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/EntityNameReference.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLAnalyzer.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLCodeAssist.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLCompletionProposal.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/IHQLCodeAssist.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/IHQLCompletionRequestor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SimpleHQLLexer.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SimpleLexerException.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SubQuery.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/RevengMetadataBuilder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/RevengMetadataCollector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/AbstractBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BasicPropertyBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BasicValueBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BinderContext.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BinderUtils.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/CollectionBinderSecondPass.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/CollectionPropertyBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/EntityPropertyBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ForeignKeyBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ForeignKeyUtils.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ManyToOneBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/OneToManyBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/OneToOneBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PrimaryKeyBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PrimaryKeyInfo.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PropertyBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/RootClassBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/TypeUtils.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/VersionPropertyBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/AbstractMetaDataDialect.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/CachedMetaDataDialect.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/H2MetaDataDialect.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/HSQLMetaDataDialect.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/JDBCMetaDataDialect.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/MySQLMetaDataDialect.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/OracleMetaDataDialect.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/ResultSetIterator.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/SQLServerMetaDataDialect.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/BasicColumnProcessor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/DatabaseReader.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/ForeignKeyProcessor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/ForeignKeysInfo.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/IndexProcessor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/PrimaryKeyProcessor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/TableCollector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/AbstractStrategy.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/DefaultStrategy.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/DelegatingStrategy.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/MetaAttributeHelper.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/OverrideBinder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/OverrideRepository.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/SQLTypeMapping.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/TableFilter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/TableSelectorStrategy.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedBasicValue.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedComponent.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedValue.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/RevengUtils.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/cfg/CfgExporter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/AbstractExporter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/ConfigurationNavigator.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/DefaultArtifactCollector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/DefaultValueVisitor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/EntityNameFromValueVisitor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/ExporterSettings.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/GenericExporter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/TemplateHelper.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/TemplateProducer.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/dao/DaoExporter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/dao/DaoHelper.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/ddl/DdlExporter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocExporter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFile.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFileManager.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFolder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocHelper.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/Cfg2HbmTool.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HBMTagForPersistentClassVisitor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HBMTagForValueVisitor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HbmExporter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HibernateMappingGlobalSettings.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/BasicPOJOClass.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/Cfg2JavaTool.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ComponentPOJOClass.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/EntityPOJOClass.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ImportContext.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ImportContextImpl.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/JavaExporter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/JavaTypeFromValueVisitor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/MetaAttributeConstants.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/MetaAttributeHelper.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/NoopImportContext.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/POJOClass.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/BadCachingDetector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/Detector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/EntityModelDetector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/HbmLint.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/HbmLintExporter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/InstrumentationDetector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/Issue.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/IssueCollector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/RelationalModelDetector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/SchemaByMetaDataDetector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/SequenceCollector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/ShadowedIdentifierDetector.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/query/QueryExporter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/JpaMetadataDescriptor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/NativeMetadataDescriptor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/RevengMetadataDescriptor.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/stat/StatisticsCellRenderer.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/stat/StatisticsTreeModel.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/AbstractTreeModel.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/AnnotationBuilder.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/BeanTableModel.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/IteratorTransformer.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/JdbcToHibernateTypeHelper.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/NameConverter.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/ReflectionUtil.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/SkipBackRefPropertyIterator.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/StringUtil.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/TableNameQualifier.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/ValueUtil.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/AbstractXMLPrettyPrinterStrategy.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/DOM3LSPrettyPrinterStrategy.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/TrAXPrettyPrinterStrategy.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/XMLPrettyPrinterStrategyFactory.java create mode 100644 tooling/hibernate-reveng/src/main/resources/dao/daohome.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/debug.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/common.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/doc-style.css create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/entities/allEntity-list.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/entities/entity.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/entities/index.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/entities/package-list.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/entities/package-summary.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/entities/perPackageEntity-list.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/entities/summary.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/hibernate_logo.gif create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/index.html create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/inherit.gif create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/tables/index.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/tables/schema-list.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/tables/schema-summary.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/tables/schema-table-list.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/tables/summary.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/tables/table-list.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/doc/tables/table.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/dot/entitygraph.dot.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/dot/tablegraph.dot.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/any.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/array.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/bag.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/collection-tableattr.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/column.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/component.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/composite-element.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/dynamic-component.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/element-element.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/filter-def.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/generalhbm.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/hibernate-mapping.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/id.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/idbag.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/import.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/key.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/list.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/many-to-any-element.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/many-to-many-element.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/many-to-one.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/map.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/meta.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/nested-composite-element.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/one-to-many-element.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/one-to-one.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/persistentclass.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/pkcolumn.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/primitive-array.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/properties.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/property.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/query.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/set.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/sql-query.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/timestamp.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/hbm/version.hbm.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/lint/text-report.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/Ejb3PropertyGetAnnotation.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/Ejb3TypeDeclaration.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/GetPropertyAnnotation.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/Pojo.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/PojoConstructors.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/PojoEqualsHashcode.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/PojoExtraClassCode.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/PojoFields.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/PojoInterfacePropertyAccessors.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/PojoPropertyAccessors.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/PojoToString.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/PojoTypeDeclaration.ftl create mode 100644 tooling/hibernate-reveng/src/main/resources/pojo/test.ftl create mode 100644 tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/core/ReverseEngineeringStrategyFactoryTest.java create mode 100644 tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/export/ExporterFactoryTest.java create mode 100644 tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/export/ExporterTypeTest.java create mode 100644 tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/version/VersionTest.java create mode 100644 tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinterTest.java create mode 100644 tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/core/binder/TypeUtilsTest.java create mode 100644 tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/core/strategy/MetaAttributeHelperTest.java create mode 100644 tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/util/ReflectionUtilTest.java create mode 100644 tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/util/StringUtilTest.java diff --git a/settings.gradle b/settings.gradle index 663f7881db6d..4437b2de841f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -359,6 +359,9 @@ project(':hibernate-maven-plugin').projectDir = new File(rootProject.projectDir, include 'hibernate-ant' project(':hibernate-ant').projectDir = new File(rootProject.projectDir, "tooling/hibernate-ant") +include 'hibernate-reveng' +project(':hibernate-reveng').projectDir = new File(rootProject.projectDir, "tooling/hibernate-reveng") + rootProject.children.each { project -> project.buildFileName = "${project.name}.gradle" assert project.projectDir.isDirectory() diff --git a/tooling/hibernate-reveng/hibernate-reveng.gradle b/tooling/hibernate-reveng/hibernate-reveng.gradle new file mode 100644 index 000000000000..ef9129c2509d --- /dev/null +++ b/tooling/hibernate-reveng/hibernate-reveng.gradle @@ -0,0 +1,14 @@ +plugins { + id "local.java-module" +} + +description = "Library providing functionality to perform reverse engineering Hibernate related artefacts from an existing database" + +dependencies { + implementation project( ':hibernate-core' ) + implementation project( ':hibernate-ant' ) + implementation "org.apache.commons:commons-collections4:4.5.0" + implementation "com.google.googlejavaformat:google-java-format:1.27.0" + implementation "org.freemarker:freemarker:2.3.34" + implementation "org.antlr:antlr4-runtime:4.13.2" +} \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/AssociationInfo.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/AssociationInfo.java new file mode 100644 index 000000000000..faee21df330a --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/AssociationInfo.java @@ -0,0 +1,27 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.core; + +public interface AssociationInfo { + + String getCascade(); + String getFetch(); + Boolean getUpdate(); + Boolean getInsert(); + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengDialect.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengDialect.java new file mode 100644 index 000000000000..9398c7c38a1e --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengDialect.java @@ -0,0 +1,126 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.core; + +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; + +import java.util.Iterator; +import java.util.Map; + +/** + * Interface for fetching metadata from databases. + * The dialect is configured with a ConnectionProvider but is not + * required to actually use any connections. + * + * The metadata methods all returns Iterator and allows for more efficient and partial reads + * for those databases that has "flakey" JDBC metadata implementions. + * + * @author Max Rydahl Andersen + * + */ +public interface RevengDialect { + + /** + * Configure the metadatadialect. + * @param connectionProvider a {@link ConnectionProvider} + */ + public void configure(ConnectionProvider connectionProvider); + + /** + * Return iterator over the tables that mathces catalog, schema and table + * + * @param catalog name or null + * @param schema name or null + * @param table name or null + * @return iterator with map elements that has "TABLE_NAME", "TABLE_SCHEMA", "TABLE_CAT", "TABLE_TYPE" keys. + */ + Iterator> getTables(String catalog, String schema, String table); + + /** + * Close the iterator. + * @param iterator an iterator returned from one of methods on this dialect + */ + void close(Iterator iterator); + + /** + * Return iterator over the indexes that mathces catalog, schema and table + * + * @param catalog name or null + * @param schema name or null + * @param table name or null + * @return iterator with map elements that has "TABLE_NAME", "TABLE_SCHEMA", "TABLE_CAT", "INDEX_NAME", "COLUMN_NAME", "NON_UNIQUE", "TYPE" keys. + */ + Iterator> getIndexInfo(String catalog, String schema, String table); + + /** + * Return iterator over the columns that mathces catalog, schema and table + * + * @param catalog name or null + * @param schema name or null + * @param table name or null + * @param column name or null + * @return iterator with map elements that has "TABLE_NAME", "TABLE_SCHEMA", "TABLE_CAT", "DATA_TYPE", "TYPE_NAME", "COLUMN_NAME", "NULLABLE", "COLUMN_SIZE", "DECIMAL_DIGITS" + */ + Iterator> getColumns(String catalog, String schema, String table, String column); + + /** + * Return iterator over the columns that mathces catalog, schema and table + * + * @param catalog name or null + * @param schema name or null + * @param table name or null + * @return iterator with map elements that has "TABLE_NAME", "TABLE_SCHEMA", "TABLE_CAT", "COLUMN_NAME", "KEY_SEQ", "PK_NAME", + */ + Iterator> getPrimaryKeys(String catalog, String schema, String name); + + + /** + * Return iterator over the exported foreign keys that mathces catalog, schema and table + * + * @param catalog name or null + * @param schema name or null + * @param table name or null + * @return iterator with map elements that has "TABLE_NAME", "TABLE_SCHEMA", "TABLE_CAT", "FKTABLE_CAT", "FKTABLE_SCHEM", "FKTABLE_NAME", "FK_NAME", "KEY_SEQ" + */ + Iterator> getExportedKeys(String catalog, String schema, String table); + + /** + * Does this name need quoting + * + * @param name + * @return + */ + boolean needQuote(String name); + + /** + * Close any resources this dialect might have used. + */ + void close(); + + /** + * Use database (possible native) metadata to suggest identifier strategy. + * + * @param catalog + * @param schema + * @param name + * @return iterator with map elements that has "TABLE_NAME", "TABLE_SCHEMA", "TABLE_CAT", "HIBERNATE_STRATEGY" (null if no possible to determine strategy, otherwise return hibernate identifier strategy name/classname) + */ + public Iterator> getSuggestedPrimaryKeyStrategyName(String catalog, String schema, String table); + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengDialectFactory.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengDialectFactory.java new file mode 100644 index 000000000000..a13898977923 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengDialectFactory.java @@ -0,0 +1,112 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.core; + +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.dialect.HSQLDialect; +import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.tool.reveng.internal.core.dialect.H2MetaDataDialect; +import org.hibernate.tool.reveng.internal.core.dialect.HSQLMetaDataDialect; +import org.hibernate.tool.reveng.internal.core.dialect.JDBCMetaDataDialect; +import org.hibernate.tool.reveng.internal.core.dialect.MySQLMetaDataDialect; +import org.hibernate.tool.reveng.internal.core.dialect.OracleMetaDataDialect; +import org.hibernate.tool.reveng.internal.core.dialect.SQLServerMetaDataDialect; + +import java.lang.reflect.Constructor; +import java.util.Properties; + +public class RevengDialectFactory { + + private RevengDialectFactory() {} + + public static RevengDialect createMetaDataDialect(Dialect dialect, Properties cfg) { + String property = cfg.getProperty( "hibernatetool.metadatadialect" ); + RevengDialect mdd = fromClassName(property); + if(mdd==null) { + mdd = fromDialect(dialect); + } + if(mdd==null) { + mdd = fromDialectName(dialect.getClass().getName()); + } + if(mdd==null) { + mdd = new JDBCMetaDataDialect(); + } + return mdd; + } + + public static RevengDialect fromClassName(String property) { + if ( property != null ) { + try { + Class revengDialectClass = ReflectHelper.classForName( + property, + RevengDialectFactory.class ); + Constructor revengDialectConstructor = revengDialectClass.getConstructor( + new Class[] {}); + return (RevengDialect)revengDialectConstructor.newInstance(); + } + catch (Throwable e) { + throw new RuntimeException( + "Could not load MetaDataDialect: " + property, e ); + } + } else { + return null; + } + } + + public static RevengDialect fromDialect(Dialect dialect) { + if(dialect!=null) { + if(dialect instanceof OracleDialect) { + return new OracleMetaDataDialect(); + } else if (dialect instanceof H2Dialect) { + return new H2MetaDataDialect(); + } else if (dialect instanceof MySQLDialect) { + return new MySQLMetaDataDialect(); + } else if (dialect instanceof HSQLDialect) { + return new HSQLMetaDataDialect(); + }else if (dialect instanceof SQLServerDialect) { + return new SQLServerMetaDataDialect(); + } + } + return null; + } + + public static RevengDialect fromDialectName(String dialect) { + if (dialect.toLowerCase().contains("oracle")) { + return new OracleMetaDataDialect(); + } + if (dialect.toLowerCase().contains("mysql")) { + return new MySQLMetaDataDialect(); + } + if (dialect.toLowerCase().contains("h2")) { + return new H2MetaDataDialect(); + } + if (dialect.toLowerCase().contains("hsql")) { + return new HSQLMetaDataDialect(); + } + if (dialect.toLowerCase().contains("sqlserver")) { + return new SQLServerMetaDataDialect(); + } + return null; + } + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengSettings.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengSettings.java new file mode 100644 index 000000000000..aa1c4e7f5969 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengSettings.java @@ -0,0 +1,110 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.core; + +public class RevengSettings { + + + final RevengStrategy rootStrategy; + + private String defaultPackageName = ""; + private boolean detectOptimisticLock = true; + private boolean createCollectionForForeignKey = true; + private boolean createManyToOneForForeignKey = true; + private boolean detectManyToMany = true; + private boolean detectOneToOne = true; + + + public RevengSettings(RevengStrategy rootStrategy) { + this.rootStrategy = rootStrategy; + } + + public RevengSettings setDefaultPackageName(String defaultPackageName) { + if(defaultPackageName==null) { + this.defaultPackageName = ""; + } else { + this.defaultPackageName= defaultPackageName.trim(); + } + return this; + } + + /** return the default packageName. Never null, at least the empty string */ + public String getDefaultPackageName() { + return defaultPackageName; + } + + /** If true, reverse engineering strategy will try and autodetect columns for optimistc locking, e.g. VERSION and TIMESTAMP */ + public boolean getDetectOptimsticLock() { + return detectOptimisticLock ; + } + + public RevengSettings setDetectOptimisticLock( + boolean optimisticLockSupportEnabled) { + this.detectOptimisticLock = optimisticLockSupportEnabled; + return this; + } + + /** if true, a collection will be mapped for each foreignkey */ + public boolean createCollectionForForeignKey() { + return createCollectionForForeignKey; + } + + + public RevengSettings setCreateCollectionForForeignKey( + boolean createCollectionForForeignKey) { + this.createCollectionForForeignKey = createCollectionForForeignKey; + return this; + } + + /** if true, a many-to-one association will be created for each foreignkey found */ + public boolean createManyToOneForForeignKey() { + return createManyToOneForForeignKey; + } + + public RevengSettings setCreateManyToOneForForeignKey( + boolean createManyToOneForForeignKey) { + this.createManyToOneForForeignKey = createManyToOneForForeignKey; + return this; + } + + public RevengSettings setDetectManyToMany(boolean b) { + this.detectManyToMany = b; + return this; + } + + public boolean getDetectManyToMany() { + return detectManyToMany; + } + + public RevengSettings setDetectOneToOne(boolean b) { + this.detectOneToOne = b; + return this; + } + + public boolean getDetectOneToOne() { + return detectOneToOne; + } + + /** return the top/root strategy. Allows a lower strategy to ask another question. Be aware of possible recursive loops; e.g. do not call the root.tableToClassName in tableToClassName of a custom reversengineeringstrategy. */ + public RevengStrategy getRootStrategy() { + return rootStrategy; + } + + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengStrategy.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengStrategy.java new file mode 100644 index 000000000000..fb2287303d69 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengStrategy.java @@ -0,0 +1,211 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.core; + +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.MetaAttribute; +import org.hibernate.mapping.Table; + +import java.util.List; +import java.util.Map; +import java.util.Properties; + +public interface RevengStrategy { + + interface SchemaSelection { + String getMatchCatalog(); + String getMatchSchema(); + String getMatchTable(); + } + + /** + * Generic method used to initialize the reverse engineering strategy. + * + * @param settings used for this + */ + public void setSettings(RevengSettings settings); + + /** + * Close any resources this strategy might have used. Called after reverse engineering has been completed. + */ + public void close(); + + /** + * + * @return a fully-qualified class name + */ + public String tableToClassName(TableIdentifier tableIdentifier); + + /** + * Return a property name for a Column. + * @param column a columnname + * @return a property name + */ + public String columnToPropertyName(TableIdentifier table, String column); + + public boolean excludeTable(TableIdentifier ti); + + public boolean excludeColumn(TableIdentifier identifier, String columnName); + + /** + * Gets the preferred Hibernate type for an SQL type. + * @param table name of the table, can be null + * @param columnName name of the column, can be null + * @param sqlType The sql type. + * @param length The length of the column. + * @param precision The number of decimal digits, if applicable. + * @param scale The scale, if applicable. + * @param nullable The nullability of the column + * @param generatedIdentifier true if for a column used in an identifier that is not "assigned", false otherwise. + * @return The Preferred hibernate type name. + */ + public String columnToHibernateTypeName(TableIdentifier table, String columnName, int sqlType, int length, int precision, int scale, boolean nullable, boolean generatedIdentifier); + + /** + * Gets the user defined foreign keys. + * @param referencedTable the table to get the foreign keys for + * @return a list of ForeignKey's + */ + public List getForeignKeys(TableIdentifier referencedTable); + + /** + * @param identifier the table to look up for + * @return the identifier strategy name wanted for a specific table, null if use what the database metadata can tell. + */ + public String getTableIdentifierStrategyName(TableIdentifier identifier); + + public Properties getTableIdentifierProperties(TableIdentifier identifier); + + /** + * If a table does not have any primarykey columns reported, this method is called. + * @return list of strings for each column name that is part of the primary key + **/ + public List getPrimaryKeyColumnNames(TableIdentifier identifier); + + /** + * Given a class name return the name for its composite id if it will have one. + * + * @param className + * @return + */ + public String classNameToCompositeIdName(String className); + + + /** Return explicit which column name should be used for optimistic lock */ + public String getOptimisticLockColumnName(TableIdentifier identifier); + + public boolean useColumnForOptimisticLock(TableIdentifier identifier, String column); + + /** + * Return list of SchemaSelctors to be used when asking {@link RevengDialect} for metadata. + * + * @return list of {@link SchemaSelection} instances + */ + public List getSchemaSelections(); + + /** + * Given a table name, return the wanted name for the identifier. + * @param tableIdentifier + * @return name to be used for identification + */ + public String tableToIdentifierPropertyName(TableIdentifier tableIdentifier); + + /** + * Given a table name, return the wanted name for a composite identifier. + * @param identifier + * @return + */ + public String tableToCompositeIdName(TableIdentifier identifier); + + /** + * Return the list of metaattributes to assign to classes created based on the given table + * @param tableIdentifier + * @return a Map from String to {@link MetaAttribute} + */ + public Map tableToMetaAttributes(TableIdentifier tableIdentifier); + + /** + * Return the list of metaattributes to assign to properties created based on the given column + * @param tableIdentifier + * @param column + * @return a Map from String to {@link MetaAttribute} + */ + public Map columnToMetaAttributes(TableIdentifier identifier, String column); + + /** Should this foreignkey be excluded as a oneToMany */ + public boolean excludeForeignKeyAsCollection(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns); + + /** Should this foreignkey be excluded as a many-to-one */ + public boolean excludeForeignKeyAsManytoOne(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns); + + /** is the collection lazy or not ? */ + public boolean isForeignKeyCollectionLazy(String name, TableIdentifier foreignKeyTable, List columns, TableIdentifier foreignKeyReferencedTable, List referencedColumns); + + /** + * Return a collection role name for a Collection based on the foreignkey. + * @param fromColumns list of Column instances on the fromTable. Only col.getName() should be assumed to be correct + * @param referencedColumns list of Column instances on the referenced Table. Only col.getName() should be assumed to be correct + * @param uniqueReference true if there is no other references to the same table + * @return + */ + public String foreignKeyToCollectionName(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns, boolean uniqueReference); + + /** + * + * @param fromColumns list of Column instances on the fromTable. Only col.getName() should be assumed to be correct + * @param referencedColumns list of Column instances on the referenced Table. Only col.getName() should be assumed to be correct + * @param uniqueReference true if there is no other references to the same table + * @return + */ + public String foreignKeyToEntityName(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns, boolean uniqueReference); + + /** + * Used to rename the inverse one-to-one properties. + * + * @param fromColumns list of Column instances on the fromTable. Only col.getName() should be assumed to be correct + * @param referencedColumns list of Column instances on the referenced Table. Only col.getName() should be assumed to be correct + * @param uniqueReference true if there is no other references to the same table + * @return null if use defaults or non-empty String with a specific name + */ + public String foreignKeyToInverseEntityName(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns, boolean uniqueReference); + + /** + * @param table + * @return true if this table is considered to be a many-to-many table. + */ + public boolean isManyToManyTable(Table table); + + /** + * + * @param middleTable + * @param uniqueReference true if there is no other references to the same table + * @param fromColumns list of Column instances on the fromTable. Only col.getName() should be assumed to be correct + * @param referencedColumns list of Column instances on the referenced Table. Only col.getName() should be assumed to be correct + * @return + */ + public String foreignKeyToManyToManyName(ForeignKey fromKey, TableIdentifier middleTable, ForeignKey toKey, boolean uniqueReference); + + public boolean isOneToOne(ForeignKey foreignKey); + + public boolean isForeignKeyCollectionInverse(String name, Table foreignKeyTable, List columns, Table foreignKeyReferencedTable, List referencedColumns); + + + public AssociationInfo foreignKeyToAssociationInfo(ForeignKey foreignKey); + public AssociationInfo foreignKeyToInverseAssociationInfo(ForeignKey foreignKey); + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengStrategyFactory.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengStrategyFactory.java new file mode 100644 index 000000000000..052172dc0ee5 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/RevengStrategyFactory.java @@ -0,0 +1,111 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2018-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.core; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import org.hibernate.tool.reveng.internal.core.strategy.DefaultStrategy; +import org.hibernate.tool.reveng.internal.core.strategy.OverrideRepository; +import org.hibernate.tool.reveng.internal.util.ReflectionUtil; +import org.jboss.logging.Logger; + +public class RevengStrategyFactory { + final private static Logger log = Logger.getLogger( RevengStrategyFactory.class ); + + private static RevengStrategy createDefaultStrategyInstance() { + return new DefaultStrategy(); + } + + private static RuntimeException createExceptionBecauseOfMissingConstructors(Class revengClass) { + return new RuntimeException("A strategy of class '" + revengClass.getName() + "' could not be created. " + + "No matching constructor found: '" + revengClass.getSimpleName() + "()' or " + + "'" + revengClass.getSimpleName() + "(" + RevengStrategy.class.getName() + ")'"); + } + + public static RevengStrategy createReverseEngineeringStrategy( + String revengClassName, RevengStrategy delegate) { + if (revengClassName == null) { + return delegate == null ? createDefaultStrategyInstance() : delegate; + } + + try { + Class revengClass = ReflectionUtil.classForName(revengClassName); + + if (delegate == null) { + try { + try { + Constructor revengConstructor = revengClass.getConstructor(); + return (RevengStrategy) revengConstructor.newInstance(); + } catch (NoSuchMethodException e1) { + try { + Constructor revengConstructor = revengClass.getConstructor(RevengStrategy.class); + return (RevengStrategy) revengConstructor.newInstance(createDefaultStrategyInstance()); + } catch (NoSuchMethodException e2) { + throw createExceptionBecauseOfMissingConstructors(revengClass); + } + } + } catch (IllegalAccessException | InstantiationException | InvocationTargetException | IllegalArgumentException e) { + throw new RuntimeException("A strategy of class '" + revengClassName + "' could not be created", e); + } + } + + try { + try { + Constructor revengConstructor = revengClass.getConstructor(RevengStrategy.class); + return (RevengStrategy) revengConstructor.newInstance(delegate); + } catch (NoSuchMethodException e1) { + log.warn("Delegating strategy given, but no matching constructor is available on class '" + + revengClassName + "'. The delegating strategy will be ignored!"); + try { + Constructor revengConstructor = revengClass.getConstructor(); + return (RevengStrategy) revengConstructor.newInstance(); + } catch (NoSuchMethodException e2) { + throw createExceptionBecauseOfMissingConstructors(revengClass); + } + } + } catch (IllegalAccessException | InstantiationException | InvocationTargetException | IllegalArgumentException e) { + throw new RuntimeException("A strategy of class '" + revengClassName + "' could not be created", e); + } + } catch (ClassNotFoundException e) { + throw new RuntimeException("A strategy of class '" + revengClassName + "' could not be created", e); + } + } + + public static RevengStrategy createReverseEngineeringStrategy( String revengClassName, File[] revengFiles) { + RevengStrategy result = null; + if (revengFiles != null && revengFiles.length > 0) { + OverrideRepository overrideRepository = new OverrideRepository(); + for (File file : revengFiles) { + overrideRepository.addFile(file); + } + result = overrideRepository.getReverseEngineeringStrategy(createDefaultStrategyInstance()); + } + + return createReverseEngineeringStrategy(revengClassName, result); + } + + public static RevengStrategy createReverseEngineeringStrategy(String revengClassName) { + return createReverseEngineeringStrategy(revengClassName, (RevengStrategy) null); + } + + public static RevengStrategy createReverseEngineeringStrategy() { + return createDefaultStrategyInstance(); + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/TableIdentifier.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/TableIdentifier.java new file mode 100644 index 000000000000..6cd229e475ec --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/core/TableIdentifier.java @@ -0,0 +1,95 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.core; + +import org.hibernate.mapping.Table; + +import java.util.Objects; + +/** + * Identifier used for the full name of tables. + * @author max + * + */ +public class TableIdentifier { + + public static TableIdentifier create(Table table) { + return new TableIdentifier(table.getCatalog(), table.getSchema(), table.getName() ); + } + + public static TableIdentifier create(String catalog, String schema, String name) { + return new TableIdentifier(catalog, schema, name); + } + + private final String catalog; + private final String schema; + private final String name; + + private TableIdentifier(String catalog, String schema, String name) { + this.catalog = (catalog==null?null:catalog.intern() ); + this.schema = (schema==null?null:schema.intern() ); + this.name = (name==null?null:name.intern() ); + } + + public String getCatalog() { + return catalog; + } + public String getName() { + return name; + } + public String getSchema() { + return schema; + } + + public boolean equals(Object obj) { + return obj instanceof TableIdentifier + && isEqualIdentifier( (TableIdentifier)obj); + } + + private boolean isEqualIdentifier(TableIdentifier otherIdentifier) { + if (otherIdentifier==null) return false; + if (this==otherIdentifier) return true; + + if (Objects.equals(this.name, otherIdentifier.name) ) { + if(Objects.equals(this.schema, otherIdentifier.schema) ) { + return Objects.equals(this.catalog, otherIdentifier.catalog); + } + } + return false; + } + + public int hashCode() { + int result = 13; + result = 37 * result + ( name==null ? 0 : name.hashCode() ); + result = 37 * result + ( schema==null ? 0 : schema.hashCode() ); + result = 37 * result + ( catalog==null ? 0 : catalog.hashCode() ); + + return result; + } + + public String toString() { + StringBuilder buf = new StringBuilder() + .append( "TableIdentifier" ) + .append('('); + if ( getCatalog()!=null ) buf.append( getCatalog() ).append( "." ); + if ( getSchema()!=null ) buf.append( getSchema() ).append( "." ); + buf.append( getName() ).append(')'); + return buf.toString(); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ArtifactCollector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ArtifactCollector.java new file mode 100644 index 000000000000..1b8d1edb0eef --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ArtifactCollector.java @@ -0,0 +1,38 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2018-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.export; + +import java.io.File; +import java.util.Set; + +public interface ArtifactCollector { + + /** + * Called to inform that a file has been created by the exporter. + */ + void addFile(File file, String type); + + int getFileCount(String type); + + File[] getFiles(String type); + + Set getFileTypes(); + + void formatFiles(); + +} \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/Exporter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/Exporter.java new file mode 100644 index 000000000000..53f0e8ae6c39 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/Exporter.java @@ -0,0 +1,36 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.export; + +import java.util.Properties; + +/** + * @author max and david + * @author koen + */ +public interface Exporter { + + + public Properties getProperties(); + + /** + * Called when exporter should start generating its output + */ + public void start(); + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterConstants.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterConstants.java new file mode 100644 index 000000000000..1d19459685ff --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterConstants.java @@ -0,0 +1,40 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2018-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.export; + +public interface ExporterConstants { + + public static final String ARTIFACT_COLLECTOR = "org.hibernate.tool.api.export.ExporterConstants.ArtifactCollector"; + public static final String CREATE_DATABASE = "org.hibernate.tool.api.export.ExporterConstants.CreateDatabase"; + public static final String DELIMITER = "org.hibernate.tool.api.export.ExporterConstants.Delimiter"; + public static final String DESTINATION_FOLDER = "org.hibernate.tool.api.export.ExporterConstants.DestinationFolder"; + public static final String DROP_DATABASE = "org.hibernate.tool.api.export.ExporterConstants.DropDatabase"; + public static final String EXPORT_TO_CONSOLE = "org.hibernate.tool.api.export.ExporterConstants.ExportToConsole"; + public static final String EXPORT_TO_DATABASE = "org.hibernate.tool.api.export.ExporterConstants.ExportToDatabase"; + public static final String FILE_PATTERN = "org.hibernate.tool.api.export.ExporterConstants.FilePattern"; + public static final String FOR_EACH = "org.hibernate.tool.api.export.ExporterConstants.ForEach"; + public static final String FORMAT = "org.hibernate.tool.api.export.ExporterConstants.Format"; + public static final String HALT_ON_ERROR = "org.hibernate.tool.api.export.ExporterConstants.HaltOnError"; + public static final String METADATA_DESCRIPTOR = "org.hibernate.tool.api.export.ExporterConstants.MetadataDescriptor"; + public static final String OUTPUT_FILE_NAME = "org.hibernate.tool.api.export.ExporterConstants.OutputFileName"; + public static final String QUERY_LIST = "org.hibernate.tool.api.export.ExporterConstants.QueryList"; + public static final String SCHEMA_UPDATE = "org.hibernate.tool.api.export.ExporterConstants.SchemaUpdate"; + public static final String TEMPLATE_NAME = "org.hibernate.tool.api.export.ExporterConstants.TemplateName"; + public static final String TEMPLATE_PATH = "org.hibernate.tool.api.export.ExporterConstants.TemplatePath"; + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterFactory.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterFactory.java new file mode 100644 index 000000000000..2d12f5d3535a --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterFactory.java @@ -0,0 +1,43 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2018-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.export; + +import org.hibernate.tool.reveng.internal.util.ReflectionUtil; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +public class ExporterFactory { + + public static Exporter createExporter(String exporterClassName) { + Exporter result = null; + try { + Class exporterClass = ReflectionUtil.classForName(exporterClassName); + Constructor exporterConstructor = exporterClass.getConstructor(new Class[] {}); + result = (Exporter)exporterConstructor.newInstance(); + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException exception) { + throw new RuntimeException("An exporter of class '" + exporterClassName + "' could not be created", exception); + } + return result; + } + + public static Exporter createExporter(ExporterType exporterType) { + return createExporter(exporterType.className()); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterType.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterType.java new file mode 100644 index 000000000000..1e25fe99fe4b --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/export/ExporterType.java @@ -0,0 +1,42 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2018-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.export; + +public enum ExporterType { + + CFG ("org.hibernate.tool.reveng.internal.export.cfg.CfgExporter"), + DAO ("org.hibernate.tool.reveng.internal.export.dao.DaoExporter"), + DDL ("org.hibernate.tool.reveng.internal.export.ddl.DdlExporter"), + DOC ("org.hibernate.tool.reveng.internal.export.doc.DocExporter"), + GENERIC ("org.hibernate.tool.reveng.internal.export.common.GenericExporter"), + HBM ("org.hibernate.tool.reveng.internal.export.hbm.HbmExporter"), + HBM_LINT ("org.hibernate.tool.reveng.internal.export.lint.HbmLintExporter"), + JAVA ("org.hibernate.tool.reveng.internal.export.java.JavaExporter"), + QUERY ("org.hibernate.tool.reveng.internal.export.query.QueryExporter"); + + private String className; + + ExporterType(String className) { + this.className = className; + } + + public String className() { + return className; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/java/DefaultJavaPrettyPrinterStrategy.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/java/DefaultJavaPrettyPrinterStrategy.java new file mode 100644 index 000000000000..dbe9a8587a21 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/java/DefaultJavaPrettyPrinterStrategy.java @@ -0,0 +1,41 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2021-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.java; + +import com.google.googlejavaformat.java.Formatter; +import com.google.googlejavaformat.java.FormatterException; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +public class DefaultJavaPrettyPrinterStrategy { + + public boolean formatFile(File file) { + try { + Formatter formatter = new Formatter(); + String toFormat = new String(Files.readAllBytes(file.toPath())); + String toWrite = formatter.formatSource(toFormat); + Files.write(file.toPath(), toWrite.getBytes()); + return true; + } catch (IOException | FormatterException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataConstants.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataConstants.java new file mode 100644 index 000000000000..6c7c82cd9222 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataConstants.java @@ -0,0 +1,24 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2020-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.metadata; + +public interface MetadataConstants { + + public static final String PREFER_BASIC_COMPOSITE_IDS = "org.hibernate.tool.api.metadata.MetadataConstants.PreferBasicCompositeIds"; + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataDescriptor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataDescriptor.java new file mode 100644 index 000000000000..2e766d0e3066 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataDescriptor.java @@ -0,0 +1,30 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2017-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.metadata; + +import org.hibernate.boot.Metadata; + +import java.util.Properties; + +public interface MetadataDescriptor { + + Metadata createMetadata(); + + Properties getProperties(); + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataDescriptorFactory.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataDescriptorFactory.java new file mode 100644 index 000000000000..c85541db7dc9 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/metadata/MetadataDescriptorFactory.java @@ -0,0 +1,52 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2017-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.metadata; + +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.internal.metadata.JpaMetadataDescriptor; +import org.hibernate.tool.reveng.internal.metadata.NativeMetadataDescriptor; +import org.hibernate.tool.reveng.internal.metadata.RevengMetadataDescriptor; + +import java.io.File; +import java.util.Properties; + +public class MetadataDescriptorFactory { + + public static MetadataDescriptor createReverseEngineeringDescriptor( + RevengStrategy reverseEngineeringStrategy, + Properties properties) { + return new RevengMetadataDescriptor( + reverseEngineeringStrategy, + properties); + } + + public static MetadataDescriptor createJpaDescriptor(String persistenceUnit, Properties properties) { + return new JpaMetadataDescriptor(persistenceUnit, properties); + } + + public static MetadataDescriptor createNativeDescriptor( + File cfgXmlFile, + File[] mappingFiles, + Properties properties) { + return new NativeMetadataDescriptor( + cfgXmlFile, + mappingFiles, + properties); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/stat/StatisticsBrowser.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/stat/StatisticsBrowser.java new file mode 100644 index 000000000000..7caa1b40e3d9 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/stat/StatisticsBrowser.java @@ -0,0 +1,128 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.stat; + +import org.hibernate.stat.Statistics; +import org.hibernate.tool.reveng.internal.stat.StatisticsCellRenderer; +import org.hibernate.tool.reveng.internal.stat.StatisticsTreeModel; +import org.hibernate.tool.reveng.internal.util.BeanTableModel; + +import javax.swing.*; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.beans.PropertyDescriptor; +import java.io.ObjectStreamClass; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Very rudimentary statistics browser. + * + * Usage: + * new StatisticsBrowser().showStatistics(getSessions().getStatistics(), shouldBlock); + * + * @author max + * + */ +public class StatisticsBrowser { + + /** + * + * @param stats a Statistics instance obtained from a SessionFactory + * @param shouldBlock decides if the ui will be modal or not. + */ + public void showStatistics(Statistics stats, boolean shouldBlock) { + + JDialog main = new JDialog((JFrame)null, "Statistics browser"); + + main.getContentPane().setLayout(new BorderLayout()); + + final StatisticsTreeModel statisticsTreeModel = new StatisticsTreeModel(stats); + JTree tree = new JTree(statisticsTreeModel); + tree.setCellRenderer( new StatisticsCellRenderer() ); + ToolTipManager.sharedInstance().registerComponent(tree); + + JScrollPane treePane = new JScrollPane(tree); + + final JTable table = new StatisticsBrowserTable(); + + JScrollPane tablePane = new JScrollPane(table); + tablePane.getViewport().setBackground( table.getBackground() ); + final BeanTableModel beanTableModel = new BeanTableModel(Collections.emptyList(), Object.class); + table.setModel( beanTableModel ); + + JSplitPane pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treePane, tablePane); + pane.setContinuousLayout( true ); + + main.getContentPane().add(pane, BorderLayout.CENTER); + + tree.addTreeSelectionListener( new TreeSelectionListener() { + + public void valueChanged(TreeSelectionEvent e) { + Object lastPathComponent = e.getPath().getLastPathComponent(); + List l = new ArrayList(); + if(statisticsTreeModel.isContainer( lastPathComponent )) { + int childCount = statisticsTreeModel.getChildCount( lastPathComponent ); + + Class cl = Object.class; + for (int i = 0; i < childCount; i++) { + Object v = statisticsTreeModel.getChild( lastPathComponent, i ); + if(v!=null) cl = v.getClass(); + l.add((PropertyDescriptor)v); + } + table.setModel( new BeanTableModel(l, cl) ); + } else { + l.add((PropertyDescriptor) lastPathComponent ); + table.setModel( new BeanTableModel(l, lastPathComponent.getClass()) ); + } + + //table.doLayout(); + + } + + } ); + + + main.getContentPane().setSize(new Dimension(640,480)); + main.pack(); + main.setModal(shouldBlock); + main.setVisible(true); + } + + final static class StatisticsBrowserTable extends JTable { + + private static final long serialVersionUID = + ObjectStreamClass.lookup(StatisticsBrowserTable.class).getSerialVersionUID(); + + public TableCellRenderer getDefaultRenderer(Class columnClass) { + TableCellRenderer defaultRenderer = + super.getDefaultRenderer( columnClass ); + if(defaultRenderer==null) { + return super.getDefaultRenderer( Object.class ); + } else { + return defaultRenderer; + } + } + } + + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/version/Version.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/version/Version.java new file mode 100644 index 000000000000..a9c5a77eb615 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/version/Version.java @@ -0,0 +1,9 @@ +package org.hibernate.tool.reveng.api.version; + +public interface Version { + + static String versionString() { + return org.hibernate.Version.getVersionString(); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinter.java new file mode 100644 index 000000000000..4e90dacf47c5 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinter.java @@ -0,0 +1,64 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.xml; + +import org.hibernate.tool.reveng.internal.xml.XMLPrettyPrinterStrategyFactory; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; + +/** + * @author max + * + */ +public final class XMLPrettyPrinter { + + public static void prettyPrintFile(File file) throws IOException { + prettyPrintFile(file, null); + } + + public static void prettyPrintFile(File file, XMLPrettyPrinterStrategy strategy) throws IOException { + String input = readFile(file.getAbsolutePath(), Charset.defaultCharset()); + String output = prettyFormat(input, strategy); + PrintWriter writer = new PrintWriter(file); + writer.print(output); + writer.flush(); + writer.close(); + } + + private static String readFile(String path, Charset encoding) throws IOException { + byte[] encoded = Files.readAllBytes(Paths.get(path)); + return new String(encoded, encoding); + } + + private static String prettyFormat(String input, XMLPrettyPrinterStrategy strategy) { + try { + if (strategy == null) { + strategy = XMLPrettyPrinterStrategyFactory.newXMLPrettyPrinterStrategy(); + } + return strategy.prettyPrint(input); + } catch (Exception e) { + throw new RuntimeException(e); // simple exception handling, please review it + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinterStrategy.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinterStrategy.java new file mode 100644 index 000000000000..35122628ad17 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinterStrategy.java @@ -0,0 +1,23 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2017-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.xml; + +public interface XMLPrettyPrinterStrategy { + + String prettyPrint(String xml) throws Exception; +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/AntlrSimpleHQLLexer.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/AntlrSimpleHQLLexer.java new file mode 100644 index 000000000000..3a0564fbf7b2 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/AntlrSimpleHQLLexer.java @@ -0,0 +1,56 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.Token; +import org.hibernate.grammars.hql.HqlLexer; + + +/** + * A lexer implemented on top of the Antlr grammer implemented in core. + * + * @author Max Rydahl Andersen + * + */ +public class AntlrSimpleHQLLexer implements SimpleHQLLexer { + + private final HqlLexer lexer; + private Token token; + + public AntlrSimpleHQLLexer(char[] cs) { + lexer = new HqlLexer(CharStreams.fromString(new String(cs))); + } + + public int getTokenLength() { + if(token.getText()==null) { + return 0; + } + return token.getText().length(); + } + + public int getTokenOffset() { + return token.getCharPositionInLine(); + } + + public int nextTokenId() { + token = lexer.nextToken(); + return token.getType(); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/CompletionHelper.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/CompletionHelper.java new file mode 100644 index 000000000000..a73e29dc7bb7 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/CompletionHelper.java @@ -0,0 +1,88 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author leon, max.andersen@jboss.com + */ +public class CompletionHelper { + + private CompletionHelper() { + } + + public static String getCanonicalPath(List qts, String name) { + Map alias2Type = new HashMap(); + for (Iterator iter = qts.iterator(); iter.hasNext();) { + EntityNameReference qt = iter.next(); + alias2Type.put(qt.getAlias(), qt.getEntityName()); + } + if (qts.size() == 1) { + EntityNameReference visible = (EntityNameReference) qts.get(0); + String alias = visible.getAlias(); + if (name.equals(alias)) { + return visible.getEntityName(); + } else if (alias == null || alias.length() == 0 || alias.equals(visible.getEntityName())) { + return visible.getEntityName() + "/" + name; + } + } + return getCanonicalPath(new HashSet(), alias2Type, name); + } + + + private static String getCanonicalPath(Set resolved, Map alias2Type, String name) { + if (resolved.contains(name)) { + // To prevent a stack overflow + return name; + } + resolved.add(name); + String type = (String) alias2Type.get(name); + if (type != null) { + return name.equals(type) ? name : getCanonicalPath(resolved, alias2Type, type); + } + int idx = name.lastIndexOf('.'); + if (idx == -1) { + return type != null ? type : name; + } + String baseName = name.substring(0, idx); + String prop = name.substring(idx + 1); + if (isAliasNown(alias2Type, baseName)) { + return getCanonicalPath(resolved, alias2Type, baseName) + "/" + prop; + } else { + return name; + } + } + + private static boolean isAliasNown(Map alias2Type, String alias) { + if (alias2Type.containsKey(alias)) { + return true; + } + int idx = alias.lastIndexOf('.'); + if (idx == -1) { + return false; + } + return isAliasNown(alias2Type, alias.substring(0, idx)); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/ConfigurationCompletion.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/ConfigurationCompletion.java new file mode 100644 index 000000000000..9cebf518d0ac --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/ConfigurationCompletion.java @@ -0,0 +1,364 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + +import org.hibernate.HibernateException; +import org.hibernate.boot.Metadata; +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.OneToMany; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.ToOne; +import org.hibernate.mapping.Value; +import org.hibernate.tool.reveng.internal.export.java.Cfg2JavaTool; +import org.hibernate.tool.reveng.internal.export.java.EntityPOJOClass; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @author Max Rydahl Andersen + * + */ +public class ConfigurationCompletion { + + private final Metadata metadata; + + public ConfigurationCompletion(Metadata md) { + this.metadata = md; + } + + public void getMatchingImports(String prefix , IHQLCompletionRequestor collector) { + getMatchingImports( prefix, prefix.length() , collector ); + } + + public void getMatchingImports(String prefix, int cursorPosition, IHQLCompletionRequestor collector) { + Iterator> iterator = metadata.getImports().entrySet().iterator(); + while ( iterator.hasNext() ) { + Entry entry = iterator.next(); + String entityImport = (String) entry.getKey(); + String entityName = (String) entry.getValue(); + + if(entityImport.toLowerCase().startsWith(prefix.toLowerCase())) { + HQLCompletionProposal proposal = createStartWithCompletionProposal( prefix, cursorPosition, HQLCompletionProposal.ENTITY_NAME, entityImport ); + proposal.setShortEntityName( entityImport ); + proposal.setEntityName( entityName ); + collector.accept(proposal); + + } + } + } + + public void getMatchingKeywords(String prefix, int cursorPosition, IHQLCompletionRequestor collector) { + findMatchingWords( cursorPosition, prefix, HQLAnalyzer.getHQLKeywords(), HQLCompletionProposal.KEYWORD, collector); + } + + public void getMatchingFunctions(String prefix, int cursorPosition, IHQLCompletionRequestor collector) { + findMatchingWords( cursorPosition, prefix, HQLAnalyzer.getHQLFunctionNames(), HQLCompletionProposal.FUNCTION, collector); + } + + public void getMatchingProperties(String path, String prefix, IHQLCompletionRequestor hcc) { + getMatchingProperties( path, prefix, prefix.length(), hcc ); + } + + public void getMatchingProperties(String path, String prefix, int cursorPosition, IHQLCompletionRequestor hcc) { + int idx = path.indexOf('/'); + if (idx == -1) { // root name + PersistentClass cmd = getPersistentClass(path); + if (cmd == null) { + return; + } + addPropertiesToList(cmd, prefix, cursorPosition, hcc); + } else { + String baseEntityName = path.substring(0, idx); + String propertyPath = path.substring(idx + 1); + Value value = getNextAttributeType(baseEntityName, propertyPath); + if (value == null) { + return; + } + + // Go to the next property (get the y of x/y/z when root is x) + idx = propertyPath.indexOf('/'); + if (idx == -1) { + path = ""; + } else { + path = propertyPath.substring(idx + 1); + } + if (path.length() == 0) { + // No properties left + if (value instanceof Component) { + addPropertiesToList((Component) value, prefix, cursorPosition, hcc); + } else if (value instanceof Collection && ((Collection)value).getElement() instanceof Component) { + addPropertiesToList((Component) ((Collection)value).getElement(), prefix, cursorPosition, hcc); + } else { + addPropertiesToList(getPersistentClass( getReferencedEntityName( value ) ), prefix, cursorPosition, hcc); + } + } else { + // Nested properties + if (value instanceof Component) { + // We need to find the first non-component type + while (value instanceof Component && path.length() > 0) { + value = getNextAttributeType((Component) value, path); + if (value != null) { + // Consume part of the canonical path + idx = path.indexOf('/'); + if (idx != -1) { + path = path.substring(idx + 1); + } else { + path = ""; + } + } + } + if (value instanceof Component) { + addPropertiesToList((Component) value, prefix, cursorPosition, hcc); + } else if (value != null) { + if (path.length() > 0) { + path = getReferencedEntityName( value ) + "/" + path; + } else { + path = getReferencedEntityName( value ); + } + getMatchingProperties( path, prefix, cursorPosition, hcc ); + } + } else { + // Just call the method recursively to add our new type + getMatchingProperties(getReferencedEntityName( value ) + "/" + path, prefix, cursorPosition, hcc); + } + } + } + } + + private String getReferencedEntityName(Value value) { + if(value instanceof ToOne) { + return ((ToOne)value).getReferencedEntityName(); + } + if ( value instanceof Collection ) { + Collection collection = ((Collection)value); + Value element = collection.getElement(); + String elementType = getReferencedEntityName( element ); + if(collection.isIndexed()) { + //TODO..list/map + /*IndexedCollection idxCol = (IndexedCollection) collection; + if(!idxCol.isList()) { + Value idxElement = idxCol.getIndex(); + String indexType = getReferencedEntityName( value ); + genericDecl = indexType + "," + elementType; + }*/ + } + return elementType; + } + + if(value instanceof OneToMany) { + return ((OneToMany)value).getReferencedEntityName(); + } + + return null; + } + + private void addPropertiesToList(PersistentClass cmd, String prefix, int cursorPosition, IHQLCompletionRequestor hcc) { + if (cmd == null) { + return; + } + if (prefix == null) { + prefix = ""; + } + + // Add superclass's properties too + while (cmd != null){ + EntityPOJOClass pc = new EntityPOJOClass(cmd, new Cfg2JavaTool()); // TODO: we should extract the needed functionallity from this hbm2java class. + + Iterator allPropertiesIterator = pc.getAllPropertiesIterator(); + while ( allPropertiesIterator.hasNext() ) { + Property property = allPropertiesIterator.next(); + String candidate = property.getName(); + if (prefix.length() == 0 || candidate.toLowerCase().startsWith(prefix.toLowerCase())) { + HQLCompletionProposal proposal = createStartWithCompletionProposal( prefix, cursorPosition, HQLCompletionProposal.PROPERTY, candidate ); + proposal.setEntityName( cmd.getEntityName() ); + proposal.setProperty( property ); + proposal.setPropertyName( candidate ); + hcc.accept( proposal); + } + } + cmd = cmd.getSuperclass(); + } + + } + + private HQLCompletionProposal createStartWithCompletionProposal(String prefix, int cursorPosition, int kind, String candidate) { + HQLCompletionProposal proposal = new HQLCompletionProposal(kind, cursorPosition); + if(candidate.startsWith(prefix)) { + proposal.setCompletion( candidate.substring(prefix.length()) ); + proposal.setSimpleName( candidate ); + proposal.setReplaceStart( cursorPosition ); + proposal.setReplaceEnd( cursorPosition ); + } else { + proposal.setCompletion( candidate ); + proposal.setSimpleName( candidate ); + proposal.setReplaceStart( cursorPosition - prefix.length() );// replace prefix + proposal.setReplaceEnd( cursorPosition ); + } + return proposal; + } + + /** returns PersistentClass for path. Can be null if path is an imported non-mapped class */ + private PersistentClass getPersistentClass(String path) { + if(path==null) return null; + String entityName = (String) metadata.getImports().get( path ); + if(entityName==null) { + return metadata.getEntityBinding(path); + } else { + return metadata.getEntityBinding(entityName); + } + } + + public String getCanonicalPath(List qts, String name) { + Map alias2Type = new HashMap(); + for (Iterator iter = qts.iterator(); iter.hasNext();) { + EntityNameReference qt = iter.next(); + alias2Type.put(qt.getAlias(), qt.getEntityName()); + } + if (qts.size() == 1) { + EntityNameReference visible = qts.get(0); + String alias = visible.getAlias(); + if (name.equals(alias)) { + return visible.getEntityName(); + } else if (alias == null || alias.length() == 0 || alias.equals(visible.getEntityName())) { + return visible.getEntityName() + "/" + name; + } + } + return getCanonicalPath(new HashSet(), alias2Type, name); + } + + private String getCanonicalPath(Set resolved, Map alias2Type, String name) { + if (resolved.contains(name)) { + // To prevent a stack overflow + return name; + } + resolved.add(name); + String type = (String) alias2Type.get(name); + if (type != null) { + return name.equals(type) ? name : getCanonicalPath(resolved, alias2Type, type); + } + int idx = name.lastIndexOf('.'); + if (idx == -1) { + return type != null ? type : name; + } + String baseName = name.substring(0, idx); + String prop = name.substring(idx + 1); + if (isAliasKnown(alias2Type, baseName)) { + return getCanonicalPath(resolved, alias2Type, baseName) + "/" + prop; + } else { + return name; + } + } + + private static boolean isAliasKnown(Map alias2Type, String alias) { + if (alias2Type.containsKey(alias)) { + return true; + } + int idx = alias.lastIndexOf('.'); + if (idx == -1) { + return false; + } + return isAliasKnown(alias2Type, alias.substring(0, idx)); + } + + private Value getNextAttributeType(String type, String attributePath) { + PersistentClass cmd = getPersistentClass( type ); + if (cmd == null) { + return null; + } + String attribute; + int idx = attributePath.indexOf('/'); + if (idx == -1) { + attribute = attributePath; + } else { + attribute = attributePath.substring(0, idx); + } + + String idName = cmd.getIdentifierProperty()==null?null:cmd.getIdentifierProperty().getName(); + if (attribute.equals(idName)) { + return cmd.getIdentifierProperty().getValue(); + } + try { + Property property = cmd.getProperty( attribute ); + return property==null?null:property.getValue(); + } catch (HibernateException he) { + return null; + } + + } + + private Value getNextAttributeType(Component t, String attributeName) { + int idx = attributeName.indexOf('/'); + if (idx != -1) { + attributeName = attributeName.substring(0, idx); + } + Iterator names = t.getProperties().iterator(); + while ( names.hasNext() ) { + Property element = (Property) names.next(); + String name = element.getName(); + if (attributeName.equals(name)) { + return element.getValue(); + } + } + return null; + } + + void addPropertiesToList(Component t, String prefix, int cursorPosition, IHQLCompletionRequestor hcc) { + if (t == null) { + return; + } + Iterator props = t.getProperties().iterator(); + while ( props.hasNext() ) { + Property element = (Property) props.next(); + String candidate = element.getName(); + if (candidate.toLowerCase().startsWith(prefix.toLowerCase())) { + HQLCompletionProposal proposal = createStartWithCompletionProposal( prefix, cursorPosition, HQLCompletionProposal.PROPERTY, candidate ); + //proposal.setEntityName( cmd.getEntityName() ); ...we don't know here..TODO: pass in the "path" + proposal.setPropertyName( candidate ); + proposal.setProperty(element); + hcc.accept( proposal); + } + } + } + + private void findMatchingWords(int cursorPosition, String prefix, String[] words, int kind, IHQLCompletionRequestor hcc) { + int i = Arrays.binarySearch(words, prefix.toLowerCase()); + if(i<0) { + i = Math.abs(i+1); + } + + for (int cnt = i; cnt < words.length; cnt++) { + String word = words[cnt]; + if(word.toLowerCase().startsWith(prefix.toLowerCase())) { + HQLCompletionProposal proposal = createStartWithCompletionProposal( prefix, cursorPosition, kind, word ); + hcc.accept( proposal); + } else { + break; + } + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/EntityNameReference.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/EntityNameReference.java new file mode 100644 index 000000000000..58dd5147053c --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/EntityNameReference.java @@ -0,0 +1,60 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + +/** + * Class that represents an alias to some entityname in a HQL statement. e.g. "Product as p" or "Product p" + * + * Should not be used by external clients. + * + * @author leon, Max Rydahl Andersen + */ +public class EntityNameReference { + + private String alias; + + private String entityName; + + public EntityNameReference(String type, String alias) { + this.entityName = type; + this.alias = alias; + } + + /** + * + * @return The alias, the "p" in "Product as p" + */ + public String getAlias() { + return alias; + } + + /** + * + * @return the entityname, the "Product" in "Product as b" + */ + public String getEntityName() { + return entityName; + } + + public String toString() { + return alias + ":" + entityName; + } + + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLAnalyzer.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLAnalyzer.java new file mode 100644 index 000000000000..b94d8187c10a --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLAnalyzer.java @@ -0,0 +1,262 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hibernate.grammars.hql.HqlLexer; + +/** + * The HQLAnalyzer can answer certain questions about a HQL String. + * + * @author leon, max.andersen@jboss.com + */ +public class HQLAnalyzer { + + /** Defines the HQL keywords. Based on hql.g antlr grammer in 2005 ;) */ + private static final String[] hqlKeywords = { "between", "class", "delete", + "desc", "distinct", "elements", "escape", "exists", "false", + "fetch", "from", "full", "group", "having", "in", "indices", + "inner", "insert", "into", "is", "join", "left", "like", "new", + "not", "null", "or", "order", "outer", "properties", "right", + "select", "set", "some", "true", "union", "update", "versioned", + "where", "and", "or", "as","on", "with", + + // -- SQL tokens -- + // These aren't part of HQL, but recognized by the lexer. Could be + // usefull for having SQL in the editor..but for now we keep them out + // "case", "end", "else", "then", "when", + + + // -- EJBQL tokens -- + "both", "empty", "leading", "member", "object", "of", "trailing", + }; + + + /** + * built-in function names. Various normal builtin functions in SQL/HQL. + * Maybe sShould try and do this dynamically based on dialect or + * sqlfunctionregistry + */ + private static final String[] builtInFunctions = { + // standard sql92 functions + "substring", "locate", "trim", "length", "bit_length", "coalesce", + "nullif", "abs", "mod", "sqrt", + "upper", + "lower", + "cast", + "extract", + + // time functions mapped to ansi extract + "second", "minute", "hour", "day", + "month", + "year", + + "str", + + // misc functions - based on oracle dialect + "sign", "acos", "asin", "atan", "cos", "cosh", "exp", "ln", "sin", + "sinh", "stddev", "sqrt", "tan", "tanh", "variance", + + "round", "trunc", "ceil", "floor", + + "chr", "initcap", "lower", "ltrim", "rtrim", "soundex", "upper", + "ascii", "length", "to_char", "to_date", + + "current_date", "current_time", "current_timestamp", "lastday", + "sysday", "systimestamp", "uid", "user", + + "rowid", "rownum", + + "concat", "instr", "instrb", "lpad", "replace", "rpad", "substr", + "substrb", "translate", + + "substring", "locate", "bit_length", "coalesce", + + "atan2", "log", "mod", "nvl", "nvl2", "power", + + "add_months", "months_between", "next_day", + + "max", "min", }; + + static { + // to allow binary search + Arrays.sort(builtInFunctions); + Arrays.sort(hqlKeywords); + } + + protected SimpleHQLLexer getLexer(char[] chars) { + return new AntlrSimpleHQLLexer(chars); + } + + /** + * Returns true if the position is at a location where an entityname makes sense. + * e.g. "from Pr| where x" + */ + public boolean shouldShowEntityNames(String query, int cursorPosition) { + return shouldShowEntityNames( query.toCharArray(), cursorPosition ); + } + + public boolean shouldShowEntityNames(char[] chars, int cursorPosition) { + SimpleHQLLexer lexer = getLexer( chars); + int tokenId = -1; + boolean show = false; + while ((tokenId = lexer.nextTokenId()) != HqlLexer.EOF) { + if ((tokenId == HqlLexer.FROM || + tokenId == HqlLexer.DELETE || + tokenId == HqlLexer.UPDATE) && + (lexer.getTokenOffset() + lexer.getTokenLength()) < cursorPosition) { + show = true; + } else if (tokenId != HqlLexer.DOT && tokenId != HqlLexer.AS && tokenId != HqlLexer.COMMA && tokenId != HqlLexer.IDENTIFIER && tokenId != HqlLexer.WS) { + show = false; + } + } + return show; + } + + public List getVisibleSubQueries(char[] chars, int position) { + SubQueryList sqList = getSubQueries(chars, position); + List visible = new ArrayList(); + for (SubQuery sq : sqList.subQueries) { + if (sqList.caretDepth >= sq.depth && (sq.startOffset <= position || sq.endOffset >= position)) { + visible.add(sq); + } + } + return visible; + } + + public List getVisibleEntityNames(char[] chars, int position) { + List sqs = getVisibleSubQueries(chars, position); + List entityReferences = new ArrayList(); + for (SubQuery sq : sqs) { + entityReferences.addAll(sq.getEntityNames()); + } + return entityReferences; + } + + public SubQueryList getSubQueries(char[] query, int position) { + SimpleHQLLexer syntax = getLexer( query ); + int numericId = -1; + List subQueries = new ArrayList(); + int depth = 0; + int caretDepth = 0; + Map level2SubQuery = new HashMap(); + SubQuery current = null; + while ((numericId = syntax.nextTokenId()) != HqlLexer.EOF) { + boolean tokenAdded = false; + if (numericId == HqlLexer.LEFT_PAREN) { + depth++; + if (position > syntax.getTokenOffset()) { + caretDepth = depth; + } + } else if (numericId == HqlLexer.RIGHT_PAREN) { + SubQuery currentDepthQuery = level2SubQuery.get(depth); + // We check if we have a query on the current depth. + // If yes, we'll have to close it + if (currentDepthQuery != null && currentDepthQuery.depth == depth) { + currentDepthQuery.endOffset = syntax.getTokenOffset(); + currentDepthQuery.tokenIds.add(numericId); + currentDepthQuery.tokenText.add(String.valueOf(query, syntax.getTokenOffset(), syntax.getTokenLength())); + subQueries.add(currentDepthQuery); + level2SubQuery.remove(depth); + tokenAdded = true; + } + depth--; + if (position > syntax.getTokenOffset()) { + caretDepth = depth; + } + } + switch (numericId) { + case HqlLexer.FROM: + case HqlLexer.UPDATE: + case HqlLexer.DELETE: + case HqlLexer.SELECT: + if (!level2SubQuery.containsKey(depth)) { + current = new SubQuery(); + current.depth = depth; + current.startOffset = syntax.getTokenOffset(); + level2SubQuery.put(depth, current); + } + if (current != null) { + current.tokenIds.add(numericId); + current.tokenText.add(String.valueOf(query, syntax.getTokenOffset(), syntax.getTokenLength())); + break; + } + default: + if (!tokenAdded) { + SubQuery sq = level2SubQuery.get(depth); + int i = depth; + while (sq == null && i >= 0) { + sq = level2SubQuery.get(i--); + } + if (sq != null) { + sq.tokenIds.add(numericId); + sq.tokenText.add(String.valueOf(query, syntax.getTokenOffset(), syntax.getTokenLength())); + } + } + } + } + for (SubQuery sq : level2SubQuery.values()) { + sq.endOffset = syntax.getTokenOffset() + syntax.getTokenLength(); + subQueries.add(sq); + } + Collections.sort(subQueries); + SubQueryList sql = new SubQueryList(); + sql.caretDepth = caretDepth; + sql.subQueries = subQueries; + return sql; + } + + + /** Returns reference name found from position and backwards in the array. + **/ + public static String getEntityNamePrefix(char[] chars, int position) { + StringBuilder buff = new StringBuilder(); + for (int i = position - 1; i >= 0; i--) { + char c = chars[i]; + if (c == '.' || Character.isJavaIdentifierPart(c)) { + buff.insert(0, c); + } else { + break; + } + } + return buff.toString(); + } + + public static class SubQueryList { + + int caretDepth; + + public List subQueries; + } + + + static String[] getHQLKeywords() { + return hqlKeywords; + } + + static String[] getHQLFunctionNames() { + return builtInFunctions; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLCodeAssist.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLCodeAssist.java new file mode 100644 index 000000000000..e0c8e782a0b7 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLCodeAssist.java @@ -0,0 +1,129 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + +import org.hibernate.boot.Metadata; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +public class HQLCodeAssist implements IHQLCodeAssist { + + private ConfigurationCompletion completion; + private Metadata metadata; + + private static final char[] charSeparators; + + static { + charSeparators = new char[]{',', '(', ')'}; + Arrays.sort(charSeparators); + } + + public HQLCodeAssist(Metadata metadata) { + this.metadata = metadata; + this.completion = new ConfigurationCompletion(metadata); + } + + public void codeComplete(String query, int position, IHQLCompletionRequestor collector) { + + int prefixStart = findNearestWhiteSpace(query, position); + String prefix = query.substring( prefixStart, position ); + + boolean showEntityNames; + try { + showEntityNames = new HQLAnalyzer().shouldShowEntityNames( query, position ); + + if(showEntityNames) { + if(hasMetadata()) { + completion.getMatchingImports( prefix, position, collector ); + } else { + collector.completionFailure("Configuration not available nor open"); + } + } else { + List visible = new HQLAnalyzer().getVisibleEntityNames( query.toCharArray(), position ); + int dotIndex = prefix.lastIndexOf("."); + if (dotIndex == -1) { + // It's a simple path, not a dot separated one (find aliases that matches) + for (Iterator iter = visible.iterator(); iter.hasNext();) { + EntityNameReference qt = iter.next(); + String alias = qt.getAlias(); + if (alias.startsWith(prefix)) { + HQLCompletionProposal completionProposal = new HQLCompletionProposal(HQLCompletionProposal.ALIAS_REF, position); + completionProposal.setCompletion( alias.substring( prefix.length() ) ); + completionProposal.setReplaceStart( position ); + completionProposal.setReplaceEnd( position+0 ); + completionProposal.setSimpleName( alias ); + completionProposal.setShortEntityName( qt.getEntityName() ); + if(hasMetadata()) { + String importedName = (String) metadata.getImports().get( qt.getEntityName() ); + completionProposal.setEntityName( importedName ); + } + collector.accept( completionProposal ); + } + } + } else { + if(hasMetadata()) { + String path = CompletionHelper.getCanonicalPath(visible, prefix.substring(0, dotIndex)); + String propertyPrefix = prefix.substring(dotIndex + 1); + completion.getMatchingProperties( path, propertyPrefix, position, collector ); + } else { + collector.completionFailure("Configuration not available nor open"); + } + } + + completion.getMatchingFunctions( prefix, position, collector ); + completion.getMatchingKeywords( prefix, position, collector ); + + + } + } catch(SimpleLexerException sle) { + collector.completionFailure( "Syntax error: " + sle.getMessage() ); + } + + } + + private boolean hasMetadata() { + return metadata!=null; + } + + public static int findNearestWhiteSpace( CharSequence doc, int start ) { + boolean loop = true; + + int offset = 0; + + int tmpOffset = start - 1; + while (loop && tmpOffset >= 0) { + char c = doc.charAt(tmpOffset); + if(isWhitespace(c)) { + loop = false; + } else { + tmpOffset--; + } + } + offset = tmpOffset + 1; + + return offset; + } + + private static boolean isWhitespace(char c) { + return Arrays.binarySearch(charSeparators, c) >= 0 + || Character.isWhitespace(c); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLCompletionProposal.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLCompletionProposal.java new file mode 100644 index 000000000000..4c01927a16c4 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/HQLCompletionProposal.java @@ -0,0 +1,217 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + +import org.hibernate.mapping.Property; + + + +public class HQLCompletionProposal { + + static final char[] NO_CHAR = new char[0]; + + public static final int ENTITY_NAME = 1; + public static final int PROPERTY = 2; + public static final int KEYWORD = 3; + public static final int FUNCTION = 4; + public static final int ALIAS_REF = 5; // ref to an alias name, e.g. "bar" in "from Bar as bar where b|" + + protected static final int FIRST_KIND = ENTITY_NAME; + protected static final int LAST_KIND = ALIAS_REF; + + /** + * kind of completion request. + */ + private int completionKind; + + /** + * original cursorposition in the query + */ + private int completionLocation; + + /** + * The actual completion. + */ + private String completion = ""; + + private int replaceStart = 0; + private int replaceEnd = 0; + + /** + * Relevance rating + */ + private int relevance = 1; + + /** The default name for the entityname, keyword, property etc. */ + private String simpleName = ""; + + /** The full related entity name, the resolved shortEntityName. Can be null */ + private String entityName = null; + + /** + * A short entity name. e.g. the imported name. + * e.g. "Product" instead of "org.hibernate.model.Product" + * (note: a imported name can also be the long version) + **/ + private String shortEntityName = null; + + /** + * The propertyName, can be null. + */ + private String propertyName = null; + + /** + * The underlying property. Can be null. + */ + private Property property; + + public String getCompletion() { + return completion; + } + + public void setCompletion(String completion) { + this.completion = completion; + } + + public int getCompletionKind() { + return completionKind; + } + + public void setCompletionKind(int completionKind) { + this.completionKind = completionKind; + } + + public int getCompletionLocation() { + return completionLocation; + } + + public void setCompletionLocation(int completionLocation) { + this.completionLocation = completionLocation; + } + + public int getRelevance() { + return relevance; + } + + public void setRelevance(int relevance) { + this.relevance = relevance; + } + + public int getReplaceEnd() { + return replaceEnd; + } + + public void setReplaceEnd(int replaceEnd) { + this.replaceEnd = replaceEnd; + } + + public int getReplaceStart() { + return replaceStart; + } + + public void setReplaceStart(int replaceStart) { + this.replaceStart = replaceStart; + } + + public HQLCompletionProposal(int kind, int cursorPosition) { + this.completionKind = kind; + this.completionLocation = cursorPosition; + } + + public String getSimpleName() { + return simpleName; + } + + public void setSimpleName(String simpleName) { + this.simpleName = simpleName; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append('['); + switch(this.completionKind) { + case ENTITY_NAME : + buffer.append("ENTITY_NAME"); + break; + case PROPERTY: + buffer.append("PROPERTY"); + break; + case KEYWORD: + buffer.append("KEYWORD"); + break; + default : + buffer.append(""); + break; + + } + buffer.append("]{completion:"); //$NON-NLS-1$ + if (this.completion != null) buffer.append(this.completion); + buffer.append(", simpleName:"); //$NON-NLS-1$ + if (this.simpleName != null) buffer.append(this.simpleName); + buffer.append(", ["); //$NON-NLS-1$ + buffer.append(this.replaceStart); + buffer.append(','); + buffer.append(this.replaceEnd); + buffer.append("], relevance="); //$NON-NLS-1$ + buffer.append(this.relevance); + buffer.append('}'); + return buffer.toString(); + } + + public String getEntityName() { + return entityName; + } + + public void setEntityName(String entityName) { + this.entityName = entityName; + } + + public String getShortEntityName() { + return shortEntityName; + } + + public void setShortEntityName(String shortEntityName) { + this.shortEntityName = shortEntityName; + } + + public String getPropertyName() { + return propertyName; + } + + public void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + + public void setProperty(Property element) { + this.property = element; + } + + public Property getProperty() { + return property; + } + + + public int aliasRefKind() { return ALIAS_REF; } + public int entityNameKind() { return ENTITY_NAME; } + public int propertyKind() { return PROPERTY; } + public int keywordKind() { return KEYWORD; } + public int functionKind() { return FUNCTION; } + + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/IHQLCodeAssist.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/IHQLCodeAssist.java new file mode 100644 index 000000000000..9968f0f09e27 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/IHQLCodeAssist.java @@ -0,0 +1,36 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + +/** + * Interface for code assist on HQL strings. + * + * @author Max Rydahl Andersen + * + */ +public interface IHQLCodeAssist { + + /** + * + * @param query the query string (full or partial) + * @param position the cursor position inside the query string + * @param requestor requestor on which the codeassist will call methods with information about proposals. + */ + void codeComplete(String query, int position, IHQLCompletionRequestor requestor); + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/IHQLCompletionRequestor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/IHQLCompletionRequestor.java new file mode 100644 index 000000000000..5a7146b45303 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/IHQLCompletionRequestor.java @@ -0,0 +1,33 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + + +/** + * The interface to implement to collect completion proposals. + * + * @author Max Rydahl Andersen + * + */ +public interface IHQLCompletionRequestor { + + boolean accept(HQLCompletionProposal proposal); + + void completionFailure(String errorMessage); + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SimpleHQLLexer.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SimpleHQLLexer.java new file mode 100644 index 000000000000..0e85d13ea4b4 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SimpleHQLLexer.java @@ -0,0 +1,34 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + +/** + * Minimal lexer interface that allows HqlAnalyzer to work. + * + * @author Max Rydahl Andersen + */ +public interface SimpleHQLLexer { + + + int nextTokenId() throws SimpleLexerException; + + int getTokenOffset(); + + int getTokenLength(); + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SimpleLexerException.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SimpleLexerException.java new file mode 100644 index 000000000000..52662420e8d3 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SimpleLexerException.java @@ -0,0 +1,49 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + +import java.io.ObjectStreamClass; + +/** + * Exception that can be thrown when the lexer encounters errors (such as syntax errors etc.) + * + * @author Max Rydahl Andersen + * + */ +public class SimpleLexerException extends RuntimeException { + + private static final long serialVersionUID = + ObjectStreamClass.lookup(SimpleLexerException.class).getSerialVersionUID(); + + public SimpleLexerException() { + super(); + } + + public SimpleLexerException(String message, Throwable cause) { + super( message, cause ); + } + + public SimpleLexerException(String message) { + super( message ); + } + + public SimpleLexerException(Throwable cause) { + super( cause ); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SubQuery.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SubQuery.java new file mode 100644 index 000000000000..2bade55217c7 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/ide/completion/SubQuery.java @@ -0,0 +1,169 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.ide.completion; + +import org.hibernate.grammars.hql.HqlLexer; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +public class SubQuery implements Comparable { + + public int compareTo(SubQuery s) { + return startOffset - s.startOffset; + } + + public boolean equals(Object s) { + if (!(s instanceof SubQuery)) return false; + return startOffset == ((SubQuery)s).startOffset; + } + + public int hashCode() { + return startOffset; + } + + List tokenIds = new ArrayList(); + + List tokenText = new ArrayList(); + + int startOffset; + + int endOffset; + + int depth; + + public int getTokenCount() { + return tokenIds.size(); + } + + public int getToken(int i) { + return tokenIds.get(i); + } + + public String getTokenText(int i) { + return (String) tokenText.get(i); + } + + public List getEntityNames() { + boolean afterFrom = false; + boolean afterJoin = false; + StringBuffer tableNames = new StringBuffer(); + StringBuffer joins = new StringBuffer(); + int i = 0; + boolean cont = true; + int lastToken = HqlLexer.EOF; + for ( Integer typeInteger : tokenIds ) { + int type = typeInteger; + if ( !cont ) { + break; + } + if ( !afterFrom && + (type == HqlLexer.FROM || + type == HqlLexer.UPDATE || + type == HqlLexer.DELETE) ) { + afterFrom = true; + } + else if ( afterJoin ) { + switch ( type ) { + case HqlLexer.ORDER: + case HqlLexer.WHERE: + case HqlLexer.GROUP: + case HqlLexer.HAVING: + cont = false; + break; + case HqlLexer.INNER: + case HqlLexer.OUTER: + case HqlLexer.LEFT: + case HqlLexer.RIGHT: + case HqlLexer.JOIN: + joins.append( "," ); + break; + case HqlLexer.COMMA: + joins.append( + "," ); //TODO: we should detect this and create the list directly instead of relying on the tokenizer + break; + case HqlLexer.DOT: + joins.append( "." ); + break; + case HqlLexer.IDENTIFIER: + if ( lastToken != HqlLexer.DOT ) { + joins.append( " " ); + } + joins.append( tokenText.get( i ) ); + break; + } + } + else if ( afterFrom ) { + switch ( type ) { + case HqlLexer.ORDER: + case HqlLexer.WHERE: + case HqlLexer.GROUP: + case HqlLexer.HAVING: + case HqlLexer.SET: + cont = false; + break; + case HqlLexer.COMMA: + tableNames.append( + "," ); //TODO: we should detect this and create the list directly instead of relying on the tokenizer + break; + case HqlLexer.DOT: + tableNames.append( "." ); + break; + case HqlLexer.IDENTIFIER: + if ( lastToken != HqlLexer.DOT ) { + tableNames.append( " " ); + } + tableNames.append( tokenText.get( i ) ); + break; + case HqlLexer.JOIN: + tableNames.append( "," ); + afterJoin = true; + break; + default: + break; + } + } + i++; + lastToken = type; + } + List tables = new ArrayList(); + addEntityReferences(tables, tableNames); + addEntityReferences(tables, joins); + return tables; + } + + private void addEntityReferences(final List tables, final StringBuffer tableNames) { + StringTokenizer tableTokenizer = new StringTokenizer(tableNames.toString(), ","); + while (tableTokenizer.hasMoreTokens()) { + String table = tableTokenizer.nextToken().trim(); + if (table.indexOf(' ') == -1 && !table.isEmpty() ) { + tables.add(new EntityNameReference(table, table)); + } else { + StringTokenizer aliasTokenizer = new StringTokenizer(table, " "); + if (aliasTokenizer.countTokens() >= 2) { + String type = aliasTokenizer.nextToken().trim(); + String alias = aliasTokenizer.nextToken().trim(); + if ( !type.isEmpty() && !alias.isEmpty() ) { + tables.add(new EntityNameReference(type, alias)); + } + } + } + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/RevengMetadataBuilder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/RevengMetadataBuilder.java new file mode 100644 index 000000000000..947a44676671 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/RevengMetadataBuilder.java @@ -0,0 +1,154 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core; + + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.internal.BootstrapContextImpl; +import org.hibernate.boot.internal.InFlightMetadataCollectorImpl; +import org.hibernate.boot.internal.MetadataBuilderImpl.MetadataBuildingOptionsImpl; +import org.hibernate.boot.internal.MetadataBuildingContextRootImpl; +import org.hibernate.boot.internal.MetadataImpl; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.boot.spi.MetadataBuildingOptions; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.RevengDialect; +import org.hibernate.tool.reveng.api.core.RevengDialectFactory; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.internal.core.binder.BinderContext; +import org.hibernate.tool.reveng.internal.core.binder.RootClassBinder; +import org.hibernate.tool.reveng.internal.core.reader.DatabaseReader; +import org.jboss.logging.Logger; + +import java.util.Properties; + + +/** + * @author max + * @author koen + */ +public class RevengMetadataBuilder { + + + public static RevengMetadataBuilder create( + Properties properties, + RevengStrategy reverseEngineeringStrategy) { + return new RevengMetadataBuilder(properties, reverseEngineeringStrategy); + } + + private static final Logger LOGGER = Logger.getLogger(RevengMetadataBuilder.class); + + private final Properties properties; + private final MetadataBuildingContext metadataBuildingContext; + private final InFlightMetadataCollectorImpl metadataCollector; + private final RevengStrategy revengStrategy; + private final BinderContext binderContext; + + private final StandardServiceRegistry serviceRegistry; + + private RevengMetadataBuilder( + Properties properties, + RevengStrategy reverseEngineeringStrategy) { + this.properties = properties; + this.revengStrategy = reverseEngineeringStrategy; + this.serviceRegistry = new StandardServiceRegistryBuilder() + .applySettings(properties) + .build(); + MetadataBuildingOptionsImpl metadataBuildingOptions = + new MetadataBuildingOptionsImpl(serviceRegistry); + BootstrapContextImpl bootstrapContext = new BootstrapContextImpl( + serviceRegistry, + metadataBuildingOptions); + metadataBuildingOptions.setBootstrapContext(bootstrapContext); + this.metadataCollector = + new InFlightMetadataCollectorImpl( + bootstrapContext, + metadataBuildingOptions); + handleTypes(bootstrapContext, metadataBuildingOptions); + this.metadataBuildingContext = new MetadataBuildingContextRootImpl( + "tools", + bootstrapContext, + metadataBuildingOptions, + metadataCollector, + null); + this.binderContext = BinderContext + .create( + metadataBuildingContext, + metadataCollector, + reverseEngineeringStrategy, + properties); + } + + public Metadata build() { + Metadata result = createMetadata(); + createPersistentClasses(readFromDatabase()); + return result; + } + + private MetadataImpl createMetadata() { + MetadataImpl result = metadataCollector.buildMetadataInstance(metadataBuildingContext); + result.getTypeConfiguration().scope(metadataBuildingContext); + return result; + } + + private RevengMetadataCollector readFromDatabase() { + RevengDialect mdd = RevengDialectFactory + .createMetaDataDialect( + serviceRegistry.getService(JdbcServices.class).getDialect(), + properties ); + DatabaseReader reader = DatabaseReader.create(properties,revengStrategy,mdd, serviceRegistry); + RevengMetadataCollector revengMetadataCollector = new RevengMetadataCollector(metadataBuildingContext); + reader.readDatabaseSchema(revengMetadataCollector); + return revengMetadataCollector; + } + + // TODO: this naively just create an entity per table + // should have an opt-out option to mark some as helper tables, subclasses etc. + /*if(table.getPrimaryKey()==null || table.getPrimaryKey().getColumnSpan()==0) { + log.warn("Cannot create persistent class for " + table + " as no primary key was found."); + continue; + // TODO: just create one big embedded composite id instead. + }*/ + private void createPersistentClasses(RevengMetadataCollector revengMetadataCollector) { + RootClassBinder rootClassBinder = RootClassBinder.create(binderContext); + for (Table table : metadataCollector.collectTableMappings()) { + if(table.getColumnSpan()==0) { + LOGGER.warn("Cannot create persistent class for " + table + " as no columns were found."); + continue; + } + if(revengStrategy.isManyToManyTable(table)) { + LOGGER.debug( "Ignoring " + table + " as class since rev.eng. says it is a many-to-many" ); + continue; + } + rootClassBinder.bind(table, revengMetadataCollector); + } + metadataCollector.processSecondPasses(metadataBuildingContext); + } + + + private static void handleTypes(BootstrapContext bootstrapContext, MetadataBuildingOptions options) { + Dialect dialect = options.getServiceRegistry().getService( JdbcServices.class ).getDialect(); + dialect.contributeTypes( () -> bootstrapContext.getTypeConfiguration(), options.getServiceRegistry() ); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/RevengMetadataCollector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/RevengMetadataCollector.java new file mode 100644 index 000000000000..a7a4bc773ef9 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/RevengMetadataCollector.java @@ -0,0 +1,116 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core; + +import org.hibernate.boot.spi.InFlightMetadataCollector; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.util.TableNameQualifier; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class RevengMetadataCollector { + + private MetadataBuildingContext metadataBuildingContext = null; + private final Map tables; + private Map> oneToManyCandidates; + private final Map suggestedIdentifierStrategies; + + public RevengMetadataCollector(MetadataBuildingContext metadataBuildingContext) { + this(); + this.metadataBuildingContext = metadataBuildingContext; + } + + public RevengMetadataCollector() { + this.tables = new HashMap(); + this.suggestedIdentifierStrategies = new HashMap(); + } + + public Iterator iterateTables() { + return tables.values().iterator(); + } + + // TableIdentifier's catalog, schema and name should be quoted + public Table addTable(TableIdentifier tableIdentifier) { + Table result = null; + String catalog = tableIdentifier.getCatalog(); + String schema = tableIdentifier.getSchema(); + String name = tableIdentifier.getName(); + InFlightMetadataCollector metadataCollector = getMetadataCollector(); + if (metadataCollector != null) { + result = metadataCollector.addTable(schema, catalog, name, null, false, metadataBuildingContext); + } else { + result = createTable(catalog, schema, name); + } + if (tables.containsKey(tableIdentifier)) { + throw new RuntimeException( + "Attempt to add a double entry for table: " + + TableNameQualifier.qualify(catalog, schema, name)); + } + tables.put(tableIdentifier, result); + return result; + } + + public Table getTable(TableIdentifier tableIdentifier) { + return tables.get(tableIdentifier); + } + + public Collection
getTables() { + return tables.values(); + } + + public void setOneToManyCandidates(Map> oneToManyCandidates) { + this.oneToManyCandidates = oneToManyCandidates; + } + + public Map> getOneToManyCandidates() { + return oneToManyCandidates; + } + + public String getSuggestedIdentifierStrategy(String catalog, String schema, String name) { + return (String) suggestedIdentifierStrategies.get(TableIdentifier.create(catalog, schema, name)); + } + + public void addSuggestedIdentifierStrategy(String catalog, String schema, String name, String idstrategy) { + suggestedIdentifierStrategies.put(TableIdentifier.create(catalog, schema, name), idstrategy); + } + + private Table createTable(String catalog, String schema, String name) { + Table table = new Table("Hibernate Tools"); + table.setAbstract(false); + table.setName(name); + table.setSchema(schema); + table.setCatalog(catalog); + return table; + } + + private InFlightMetadataCollector getMetadataCollector() { + InFlightMetadataCollector result = null; + if (metadataBuildingContext != null) { + result = metadataBuildingContext.getMetadataCollector(); + } + return result; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/AbstractBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/AbstractBinder.java new file mode 100644 index 000000000000..e11442aa71ec --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/AbstractBinder.java @@ -0,0 +1,58 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.boot.spi.InFlightMetadataCollector; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.tool.reveng.api.metadata.MetadataConstants; +import org.hibernate.tool.reveng.api.core.RevengStrategy; + +abstract class AbstractBinder { + + final BinderContext binderContext; + + AbstractBinder(BinderContext binderContext) { + this.binderContext = binderContext; + } + + MetadataBuildingContext getMetadataBuildingContext() { + return binderContext.metadataBuildingContext; + } + + InFlightMetadataCollector getMetadataCollector() { + return binderContext.metadataCollector; + } + + RevengStrategy getRevengStrategy() { + return binderContext.revengStrategy; + } + + String getDefaultCatalog() { + return binderContext.properties.getProperty(AvailableSettings.DEFAULT_CATALOG); + } + + String getDefaultSchema() { + return binderContext.properties.getProperty(AvailableSettings.DEFAULT_SCHEMA); + } + + Boolean preferBasicCompositeIds() { + return (Boolean)binderContext.properties.get(MetadataConstants.PREFER_BASIC_COMPOSITE_IDS); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BasicPropertyBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BasicPropertyBinder.java new file mode 100644 index 000000000000..158b9787fcd1 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BasicPropertyBinder.java @@ -0,0 +1,49 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.internal.core.util.RevengUtils; + +class BasicPropertyBinder extends AbstractBinder { + + static BasicPropertyBinder create(BinderContext binderContext) { + return new BasicPropertyBinder(binderContext); + } + + private final BasicValueBinder simpleValueBinder; + private final PropertyBinder propertyBinder; + + private BasicPropertyBinder(BinderContext binderContext) { + super(binderContext); + simpleValueBinder = BasicValueBinder.create(binderContext); + propertyBinder = PropertyBinder.create(binderContext); + } + + + Property bind(String propertyName, Table table, Column column) { + return propertyBinder.bind( + table, + propertyName, + simpleValueBinder.bind(table, column, false), + RevengUtils.createAssociationInfo(null, null, true, true)); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BasicValueBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BasicValueBinder.java new file mode 100644 index 000000000000..9dde6983d871 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BasicValueBinder.java @@ -0,0 +1,52 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.internal.core.util.EnhancedBasicValue; + +class BasicValueBinder extends AbstractBinder { + + static BasicValueBinder create(BinderContext binderContext) { + return new BasicValueBinder(binderContext); + } + + private BasicValueBinder(BinderContext binderContext) { + super(binderContext); + } + + EnhancedBasicValue bind( + Table table, + Column column, + boolean generatedIdentifier) { + EnhancedBasicValue value = new EnhancedBasicValue(getMetadataBuildingContext(), table); + value.addColumn(column); + value.setTypeName(TypeUtils.determinePreferredType( + getMetadataCollector(), + getRevengStrategy(), + table, + column, + generatedIdentifier)); + if (generatedIdentifier) { + value.setNullValue("undefined"); + } + return value; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BinderContext.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BinderContext.java new file mode 100644 index 000000000000..8de47378c1d5 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BinderContext.java @@ -0,0 +1,56 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.boot.spi.InFlightMetadataCollector; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.tool.reveng.api.core.RevengStrategy; + +import java.util.Properties; + +public class BinderContext { + + public static BinderContext create( + MetadataBuildingContext metadataBuildingContext, + InFlightMetadataCollector metadataCollector, + RevengStrategy revengStrategy, + Properties properties) { + return new BinderContext( + metadataBuildingContext, + metadataCollector, + revengStrategy, + properties); + } + + public final MetadataBuildingContext metadataBuildingContext; + public final InFlightMetadataCollector metadataCollector; + public final RevengStrategy revengStrategy; + public final Properties properties; + + private BinderContext( + MetadataBuildingContext metadataBuildingContext, + InFlightMetadataCollector metadataCollector, + RevengStrategy revengStrategy, + Properties properties) { + this.metadataBuildingContext = metadataBuildingContext; + this.metadataCollector = metadataCollector; + this.revengStrategy = revengStrategy; + this.properties = properties; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BinderUtils.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BinderUtils.java new file mode 100644 index 000000000000..790cfbe86a6a --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/BinderUtils.java @@ -0,0 +1,108 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.FetchMode; +import org.hibernate.internal.util.collections.JoinedList; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.Fetchable; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.tool.reveng.api.core.AssociationInfo; +import org.hibernate.tool.reveng.api.core.RevengStrategy; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class BinderUtils { + + public static Logger LOGGER = Logger.getLogger(BinderUtils.class.getName()); + + public static String makeUnique( + Iterator props, + String originalPropertyName) { + int cnt = 0; + String propertyName = originalPropertyName; + Set uniqueNames = new HashSet(); + while ( props.hasNext() ) { + Property element = props.next(); + uniqueNames.add( element.getName() ); + } + while( uniqueNames.contains(propertyName) ) { + cnt++; + propertyName = originalPropertyName + "_" + cnt; + } + return propertyName; + } + + public static String makeUnique(PersistentClass clazz, String propertyName) { + List list = new ArrayList(); + if( clazz.hasIdentifierProperty() ) { + list.add( clazz.getIdentifierProperty() ); + } + if( clazz.isVersioned() ) { + list.add( clazz.getVersion() ); + } + JoinedList joinedList = + new JoinedList( + list, + clazz.getProperties()); + return BinderUtils.makeUnique(joinedList.iterator(), propertyName); + } + + public static String makeUnique(Component clazz, String propertyName) { + return BinderUtils.makeUnique(clazz.getProperties().iterator(), propertyName); + } + + public static void checkColumnForMultipleBinding(Column column) { + if(column.getValue()!=null) { + LOGGER.log(Level.WARNING, "Binding column twice should not happen. " + column); +// TODO enable this next line and investigate why the tests fail +// throw new RuntimeException("Binding column twice should not happen. " + column); + } + } + + static void updateFetchMode(Fetchable value, String fetchMode) { + if(FetchMode.JOIN.toString().equalsIgnoreCase(fetchMode)) { + value.setFetchMode(FetchMode.JOIN); + } + else { + value.setFetchMode(FetchMode.SELECT); + } + } + + + static AssociationInfo getAssociationInfo( + RevengStrategy revengStrategy, + ForeignKey foreignKey, + boolean inverseProperty) { + if (inverseProperty) { + return revengStrategy.foreignKeyToInverseAssociationInfo(foreignKey); + } else { + return revengStrategy.foreignKeyToAssociationInfo(foreignKey); + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/CollectionBinderSecondPass.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/CollectionBinderSecondPass.java new file mode 100644 index 000000000000..ca994e9b9b1f --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/CollectionBinderSecondPass.java @@ -0,0 +1,106 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.MappingException; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.DependantValue; +import org.hibernate.mapping.OneToMany; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Value; + +import java.io.ObjectStreamClass; +import java.lang.reflect.Field; +import java.util.Map; + +public class CollectionBinderSecondPass extends org.hibernate.boot.model.internal.CollectionSecondPass { + + private static final long serialVersionUID = + ObjectStreamClass.lookup( CollectionBinderSecondPass.class).getSerialVersionUID(); + + private MetadataBuildingContext mdbc; + + public CollectionBinderSecondPass(MetadataBuildingContext mdbc, Collection coll) { + super(coll); + this.mdbc = mdbc; + } + + @SuppressWarnings("rawtypes") + public void secondPass(Map persistentClasses) throws MappingException { + bindCollectionSecondPass(getCollection(), mdbc); + } + + public void doSecondPass(Map persistentClasses) throws MappingException { + Value element = getCollection().getElement(); + DependantValue elementDependantValue = null; + String oldElementForeignKeyName = null; + if(element instanceof DependantValue) { + elementDependantValue = (DependantValue)element; + oldElementForeignKeyName = elementDependantValue.getForeignKeyName(); + elementDependantValue.setForeignKeyName("none"); // Workaround to avoid DependantValue to create foreignkey just because reference columns are not the same + no need to create keys already in the db! + } + Value key = getCollection().getKey(); + DependantValue keyDependantValue = null; + String oldKeyForeignKeyName = null; + if (key instanceof DependantValue) { + keyDependantValue = (DependantValue)key; + oldKeyForeignKeyName = keyDependantValue.getForeignKeyName(); + keyDependantValue.setForeignKeyName("none"); + } + secondPass(persistentClasses); +// super.doSecondPass(persistentClasses); + if(elementDependantValue!=null) { + elementDependantValue.setForeignKeyName(oldElementForeignKeyName); + } + if (keyDependantValue != null) { + keyDependantValue.setForeignKeyName(oldKeyForeignKeyName); + } + } + + private void bindCollectionSecondPass( + Collection collection, + MetadataBuildingContext mdbc) throws MappingException { + if(collection.isOneToMany() ) { + OneToMany oneToMany = (OneToMany) collection.getElement(); + PersistentClass persistentClass = mdbc.getMetadataCollector().getEntityBinding(oneToMany.getReferencedEntityName()); + + if (persistentClass==null) throw new MappingException( + "Association " + collection.getRole() + " references unmapped class: " + oneToMany.getReferencedEntityName() + ); + + oneToMany.setAssociatedClass(persistentClass); // Child + } + } + + private Collection getCollection() { + try { + Field field = getClass().getSuperclass().getDeclaredField("collection"); + field.setAccessible(true); + return (Collection)field.get(this); + } catch (NoSuchFieldException e) { + // this will happen if the implementation of the superclass changes + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + // this should not happen + throw new RuntimeException(e); + } + } + +} + diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/CollectionPropertyBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/CollectionPropertyBinder.java new file mode 100644 index 000000000000..305f5f877857 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/CollectionPropertyBinder.java @@ -0,0 +1,70 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.AssociationInfo; +import org.hibernate.tool.reveng.internal.core.util.RevengUtils; + +class CollectionPropertyBinder extends AbstractBinder { + + static CollectionPropertyBinder create(BinderContext binderContext) { + return new CollectionPropertyBinder(binderContext); + } + + private final PropertyBinder propertyBinder; + + private CollectionPropertyBinder(BinderContext binderContext) { + super(binderContext); + this.propertyBinder = PropertyBinder.create(binderContext); + } + + Property bind( + String propertyName, + boolean mutable, + Table table, + ForeignKey fk, + Collection value, + boolean inverseProperty) { + AssociationInfo associationInfo = determineAssociationInfo(fk, inverseProperty, mutable); + BinderUtils.updateFetchMode(value, associationInfo.getFetch()); + return propertyBinder.bind(table, propertyName, value, associationInfo); + } + + private AssociationInfo determineAssociationInfo( + ForeignKey foreignKey, + boolean inverseProperty, + boolean mutable) { + AssociationInfo origin = BinderUtils + .getAssociationInfo(getRevengStrategy(), foreignKey, inverseProperty); + if(origin != null){ + return RevengUtils.createAssociationInfo( + origin.getCascade() != null ? origin.getCascade() : "all", + origin.getFetch(), + origin.getInsert() != null ? origin.getInsert() : mutable, + origin.getUpdate() != null ? origin.getUpdate() : mutable + ); + } else { + return RevengUtils.createAssociationInfo(null, null, mutable, mutable); + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/EntityPropertyBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/EntityPropertyBinder.java new file mode 100644 index 000000000000..a96393199370 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/EntityPropertyBinder.java @@ -0,0 +1,70 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Table; +import org.hibernate.mapping.ToOne; +import org.hibernate.tool.reveng.api.core.AssociationInfo; +import org.hibernate.tool.reveng.internal.core.util.RevengUtils; + +class EntityPropertyBinder extends AbstractBinder { + + static EntityPropertyBinder create(BinderContext binderContext) { + return new EntityPropertyBinder(binderContext); + } + + private final PropertyBinder propertyBinder; + + private EntityPropertyBinder(BinderContext binderContext) { + super(binderContext); + this.propertyBinder = PropertyBinder.create(binderContext); + } + + Property bind( + String propertyName, + boolean mutable, + Table table, + ForeignKey fk, + ToOne value, + boolean inverseProperty) { + AssociationInfo associationInfo = determineAssociationInfo(fk, inverseProperty, mutable); + BinderUtils.updateFetchMode(value, associationInfo.getFetch()); + return propertyBinder.bind(table, propertyName, value, associationInfo); + } + + private AssociationInfo determineAssociationInfo( + ForeignKey foreignKey, + boolean inverseProperty, + boolean mutable) { + AssociationInfo origin = BinderUtils + .getAssociationInfo(getRevengStrategy(), foreignKey, inverseProperty); + if(origin != null){ + return RevengUtils.createAssociationInfo( + origin.getCascade(), + origin.getFetch(), + origin.getInsert() != null ? origin.getInsert() : mutable, + origin.getUpdate() != null ? origin.getUpdate() : mutable + ); + } else { + return RevengUtils.createAssociationInfo(null, null, mutable, mutable); + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ForeignKeyBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ForeignKeyBinder.java new file mode 100644 index 000000000000..e2bc8f430410 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ForeignKeyBinder.java @@ -0,0 +1,146 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.mapping.Column; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.TableIdentifier; + +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +class ForeignKeyBinder extends AbstractBinder { + + private static Logger LOGGER = Logger.getLogger(ForeignKeyBinder.class.getName()); + + static ForeignKeyBinder create(BinderContext binderContext) { + return new ForeignKeyBinder(binderContext); + } + + private final OneToOneBinder oneToOneBinder; + private final OneToManyBinder oneToManyBinder; + private final ManyToOneBinder manyToOneBinder; + + private ForeignKeyBinder(BinderContext binderContext) { + super(binderContext); + this.oneToOneBinder = OneToOneBinder.create(binderContext); + this.oneToManyBinder = OneToManyBinder.create(binderContext); + this.manyToOneBinder = ManyToOneBinder.create(binderContext); + } + + void bindIncoming( + ForeignKey foreignKey, + PersistentClass persistentClass, + Set processed) { + if(excludeForeignKeyAsCollection(foreignKey)) { + LOGGER.log(Level.INFO, "Rev.eng excluded one-to-many or one-to-one for foreignkey " + foreignKey.getName()); + } else if (getRevengStrategy().isOneToOne(foreignKey)){ + addOneToOne(foreignKey, persistentClass, processed, false); + } else { + addOneToMany(foreignKey, persistentClass); + } + } + + void bindOutgoing( + ForeignKey foreignKey, + Table table, + PersistentClass rc, + Set processedColumns, + boolean mutable) { + if(excludeForeignKeyAsManyToOne(foreignKey)) { + // TODO: if many-to-one is excluded should the column be marked as processed so it won't show up at all ? + LOGGER.log(Level.INFO, "Rev.eng excluded *-to-one for foreignkey " + foreignKey.getName()); + } else if (isOneToOne(foreignKey)){ + addOneToOne(foreignKey, rc, processedColumns, true); + } else { + addManyToOne(foreignKey, table, rc, processedColumns, mutable); + } + } + + private String getForeignKeyToEntityName(ForeignKey foreignKey) { + return getRevengStrategy().foreignKeyToEntityName( + foreignKey.getName(), + TableIdentifier.create(foreignKey.getTable() ), + foreignKey.getColumns(), + TableIdentifier.create(foreignKey.getReferencedTable() ), + foreignKey.getReferencedColumns(), + ForeignKeyUtils.isUniqueReference(foreignKey)); + } + + private void addManyToOne( + ForeignKey foreignKey, + Table table, + PersistentClass rc, + Set processedColumns, + boolean mutable) { + Property property = manyToOneBinder.bind( + BinderUtils.makeUnique(rc, getForeignKeyToEntityName(foreignKey)), + mutable, + table, + foreignKey, + processedColumns); + rc.addProperty(property); + } + + private void addOneToOne( + ForeignKey foreignKey, + PersistentClass rc, + Set processedColumns, + boolean outgoing) { + Table table = outgoing ? foreignKey.getReferencedTable() : foreignKey.getTable(); + Property property = oneToOneBinder.bind( + rc, + table, + foreignKey, + processedColumns, + outgoing, + !outgoing); + rc.addProperty(property); + } + + private void addOneToMany(ForeignKey foreignKey, PersistentClass persistentClass) { + persistentClass.addProperty(oneToManyBinder.bind(persistentClass, foreignKey)); + } + + private boolean excludeForeignKeyAsCollection(ForeignKey foreignKey) { + return getRevengStrategy().excludeForeignKeyAsCollection( + foreignKey.getName(), + TableIdentifier.create(foreignKey.getTable() ), + foreignKey.getColumns(), + TableIdentifier.create(foreignKey.getReferencedTable() ), + foreignKey.getReferencedColumns()); + } + + private boolean excludeForeignKeyAsManyToOne(ForeignKey foreignKey) { + return getRevengStrategy().excludeForeignKeyAsManytoOne( + foreignKey.getName(), + TableIdentifier.create(foreignKey.getTable() ), + foreignKey.getColumns(), + TableIdentifier.create(foreignKey.getReferencedTable()), + foreignKey.getReferencedColumns()); + } + + private boolean isOneToOne(ForeignKey foreignKey) { + return getRevengStrategy().isOneToOne(foreignKey); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ForeignKeyUtils.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ForeignKeyUtils.java new file mode 100644 index 000000000000..a3a744b31690 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ForeignKeyUtils.java @@ -0,0 +1,91 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.mapping.Column; +import org.hibernate.mapping.ForeignKey; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class ForeignKeyUtils { + + public static boolean isUniqueReference(ForeignKey foreignKey) { + for (ForeignKey element : foreignKey.getTable().getForeignKeys().values()) { + if(element!=foreignKey && element.getReferencedTable().equals(foreignKey.getReferencedTable())) { + return false; + } + } + return true; + } + + public static List findForeignKeys(Collection foreignKeys, List pkColumns) { + List tempList = new ArrayList(); + for (ForeignKey fk : foreignKeys) { + tempList.add(fk); + } + List result = new ArrayList(); + Column[] myPkColumns = pkColumns.toArray(new Column[pkColumns.size()]); + for (int i = 0; i < myPkColumns.length; i++) { + boolean foundKey = false; + for (ForeignKey key : tempList) { + List matchingColumns = columnMatches(myPkColumns, i, key); + if(!matchingColumns.isEmpty()) { + result.add(new ForeignKeyForColumns(key, matchingColumns)); + i+=matchingColumns.size()-1; + foundKey=true; + break; + } + } + if(!foundKey) { + result.add(myPkColumns[i]); + } + } + return result; + } + + private static List columnMatches( + Column[] pkColumns, + int offset, + ForeignKey fk) { + List result = new ArrayList(); + int columnSpan = fk.getColumnSpan(); + if (columnSpan <= pkColumns.length-offset) { + for (int i = 0; i < columnSpan; i++) { + Column column = pkColumns[i + offset]; + if(column.equals(fk.getColumn(i))) { + result.add(column); + } else { + result.clear(); + break; + } + } + } + return result; + } + + public static class ForeignKeyForColumns { + public final List columns; + public final ForeignKey key; + public ForeignKeyForColumns(ForeignKey key, List columns) { + this.key = key; + this.columns = columns; + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ManyToOneBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ManyToOneBinder.java new file mode 100644 index 000000000000..95cb820e2621 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/ManyToOneBinder.java @@ -0,0 +1,73 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.FetchMode; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.ManyToOne; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Table; + +import java.util.Set; + +class ManyToOneBinder extends AbstractBinder { + + static ManyToOneBinder create(BinderContext binderContext) { + return new ManyToOneBinder(binderContext); + } + + private final EntityPropertyBinder entityPropertyBinder; + + private ManyToOneBinder(BinderContext binderContext) { + super(binderContext); + this.entityPropertyBinder = EntityPropertyBinder.create(binderContext); + } + + Property bind( + String propertyName, + boolean mutable, + Table table, + ForeignKey fk, + Set processedColumns) { + ManyToOne value = new ManyToOne(getMetadataBuildingContext(), table); + value.setReferencedEntityName( fk.getReferencedEntityName() ); + addColumns(value, fk, processedColumns); + value.setFetchMode(FetchMode.SELECT); + return entityPropertyBinder + .bind( + propertyName, + mutable, + table, + fk, + value, + false); + } + + private void addColumns( + ManyToOne value, + ForeignKey fk, + Set processedColumns) { + for (Column fkcolumn : fk.getColumns()) { + BinderUtils.checkColumnForMultipleBinding(fkcolumn); + value.addColumn(fkcolumn); + processedColumns.add(fkcolumn); + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/OneToManyBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/OneToManyBinder.java new file mode 100644 index 000000000000..a8b18ab617d8 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/OneToManyBinder.java @@ -0,0 +1,217 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.FetchMode; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.DependantValue; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.KeyValue; +import org.hibernate.mapping.ManyToOne; +import org.hibernate.mapping.OneToMany; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.TableIdentifier; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +class OneToManyBinder extends AbstractBinder { + + static OneToManyBinder create(BinderContext binderContext) { + return new OneToManyBinder(binderContext); + } + + private final CollectionPropertyBinder collectionPropertyBinder; + + private OneToManyBinder(BinderContext binderContext) { + super(binderContext); + this.collectionPropertyBinder = CollectionPropertyBinder.create(binderContext); + } + + Property bind(PersistentClass rc, ForeignKey foreignKey) { + Collection collection = bindCollection(rc, foreignKey); + getMetadataCollector().addCollectionBinding(collection); + return collectionPropertyBinder + .bind( + StringHelper.unqualify(collection.getRole()), + true, + rc.getTable(), + foreignKey, + collection, + true); + } + + private Collection bindCollection(PersistentClass pc, ForeignKey foreignKey) { + Table table = foreignKey.getTable(); + Collection collection = new org.hibernate.mapping.Set(getMetadataBuildingContext(), pc); + collection.setCollectionTable(table); + boolean manyToMany = getRevengStrategy().isManyToManyTable( table ); + if(manyToMany) { + bindManyToMany(pc, foreignKey, collection); + } else { + bindOneToMany(pc, foreignKey, collection); + } + collection.setKey(createKeyValue(table, foreignKey, getReferencedKeyValue(collection))); + return collection; + } + + private KeyValue createKeyValue(Table collectionTable, ForeignKey foreignKey, KeyValue referencedKeyValue) { + SimpleValue keyValue = new DependantValue(getMetadataBuildingContext(), collectionTable, referencedKeyValue); + //keyValue.setForeignKeyName("none"); // Avoid creating the foreignkey + //key.setCascadeDeleteEnabled( "cascade".equals( subnode.attributeValue("on-delete") ) ); + for (Column fkcolumn : foreignKey.getColumns()) { + if(fkcolumn.getSqlTypeCode()!=null) { // TODO: user defined foreign ref columns does not have a type set. + TypeUtils.determinePreferredType( + getMetadataCollector(), + getRevengStrategy(), + collectionTable, + fkcolumn, + false); // needed to ensure foreign key columns has same type as the "property" column. + } + keyValue.addColumn( fkcolumn ); + } + return keyValue; + } + + private KeyValue getReferencedKeyValue(Collection collection) { + String propRef = collection.getReferencedPropertyName(); + if (propRef==null) { + return collection.getOwner().getIdentifier(); + } + else { + return (KeyValue)collection.getOwner().getProperty(propRef).getValue(); + } + } + + private void bindManyToMany(PersistentClass pc, ForeignKey fromForeignKey, Collection collection) { + ForeignKey toForeignKey = getToForeignKey(fromForeignKey); + bindCollection( pc, fromForeignKey, toForeignKey, collection ); + ManyToOne manyToOne = new ManyToOne(getMetadataBuildingContext(), collection.getCollectionTable()); + manyToOne.setReferencedEntityName(getTableToClassName(toForeignKey.getReferencedTable())); + addColumns(manyToOne, toForeignKey); + collection.setElement(manyToOne); + } + + private void addColumns(ManyToOne manyToOne, ForeignKey fk) { + Iterator columnIterator = fk.getColumns().iterator(); + while (columnIterator.hasNext()) { + Column fkcolumn = (Column) columnIterator.next(); + if(fkcolumn.getSqlTypeCode() != null) { // TODO: user defined foreign ref columns does not have a type set. + TypeUtils.determinePreferredType( + getMetadataCollector(), + getRevengStrategy(), + fk.getTable(), + fkcolumn, + false); // needed to ensure foreign key columns has same type as the "property" column. + } + manyToOne.addColumn(fkcolumn); + } + } + + private ForeignKey getToForeignKey(ForeignKey fromForeignKey) { + List keys = new ArrayList(); + for (ForeignKey foreignKey : fromForeignKey.getTable().getForeignKeys().values()) { + if(foreignKey!=fromForeignKey) { + keys.add(foreignKey); + } + } + if(keys.size()>1) { + throw new RuntimeException("more than one other foreign key to choose from!"); // todo: handle better ? + } + return (ForeignKey) keys.get( 0 ); + } + + private void bindOneToMany(PersistentClass pc, ForeignKey fromForeignKey, Collection collection) { + bindCollection(pc, fromForeignKey, null, collection); + OneToMany oneToMany = new OneToMany(getMetadataBuildingContext(), collection.getOwner()); + oneToMany.setReferencedEntityName(getTableToClassName(fromForeignKey.getTable())); + getMetadataCollector().addSecondPass( + new CollectionBinderSecondPass(getMetadataBuildingContext(), collection)); + collection.setElement(oneToMany); + } + + + private void bindCollection(PersistentClass pc, ForeignKey fromForeignKey, ForeignKey toForeignKey, Collection collection) { + ForeignKey targetKey = toForeignKey != null ? toForeignKey : fromForeignKey; + collection.setRole(getFullRolePath(pc, fromForeignKey, toForeignKey)); + collection.setInverse(isCollectionInverse(targetKey)); + collection.setLazy(isCollectionLazy(targetKey)); + collection.setFetchMode(FetchMode.SELECT); + } + + private boolean isCollectionLazy(ForeignKey foreignKey) { + return getRevengStrategy().isForeignKeyCollectionLazy( + foreignKey.getName(), + TableIdentifier.create( foreignKey.getTable()), + foreignKey.getColumns(), + TableIdentifier.create(foreignKey.getReferencedTable()), + foreignKey.getReferencedColumns()); + } + + private boolean isCollectionInverse(ForeignKey foreignKey) { + return getRevengStrategy().isForeignKeyCollectionInverse( + foreignKey.getName(), + foreignKey.getTable(), + foreignKey.getColumns(), + foreignKey.getReferencedTable(), + foreignKey.getReferencedColumns()); + } + + private String getTableToClassName(Table table) { + return getRevengStrategy().tableToClassName(TableIdentifier.create(table)); + } + + private String getFullRolePath( + PersistentClass pc, + ForeignKey fromForeignKey, + ForeignKey toForeignKey) { + String collectionRole = null; + if(toForeignKey==null) { + collectionRole = getForeignKeyToCollectionName(fromForeignKey); + } else { + collectionRole = getForeignKeyToManyToManyName(fromForeignKey, toForeignKey); + } + collectionRole = BinderUtils.makeUnique(pc,collectionRole); + return StringHelper.qualify(pc.getEntityName(), collectionRole); + } + + private String getForeignKeyToCollectionName(ForeignKey foreignKey) { + return getRevengStrategy().foreignKeyToCollectionName( + foreignKey.getName(), + TableIdentifier.create(foreignKey.getTable()), + foreignKey.getColumns(), + TableIdentifier.create( foreignKey.getReferencedTable()), + foreignKey.getReferencedColumns(), + ForeignKeyUtils.isUniqueReference(foreignKey)); + } + + private String getForeignKeyToManyToManyName(ForeignKey fromForeignKey, ForeignKey toForeignKey) { + return getRevengStrategy().foreignKeyToManyToManyName( + fromForeignKey, + TableIdentifier.create(fromForeignKey.getTable()), + toForeignKey, + ForeignKeyUtils.isUniqueReference(toForeignKey)); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/OneToOneBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/OneToOneBinder.java new file mode 100644 index 000000000000..0eb580cf14f4 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/OneToOneBinder.java @@ -0,0 +1,108 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.FetchMode; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.OneToOne; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.type.ForeignKeyDirection; + +import java.util.Set; + +class OneToOneBinder extends AbstractBinder { + + static OneToOneBinder create(BinderContext binderContext) { + return new OneToOneBinder(binderContext); + } + + private final EntityPropertyBinder entityPropertyBinder; + + private OneToOneBinder(BinderContext binderContext) { + super(binderContext); + this.entityPropertyBinder = EntityPropertyBinder.create(binderContext); + } + + Property bind( + PersistentClass rc, + Table targetTable, + ForeignKey fk, + Set processedColumns, + boolean constrained, + boolean inverseProperty) { + OneToOne value = new OneToOne(getMetadataBuildingContext(), targetTable, rc); + value.setReferencedEntityName( + getRevengStrategy().tableToClassName(TableIdentifier.create(targetTable))); + addColumns(fk, value, processedColumns); + value.setFetchMode(FetchMode.SELECT); + value.setConstrained(constrained); + value.setForeignKeyType( + constrained ? + ForeignKeyDirection.FROM_PARENT : + ForeignKeyDirection.TO_PARENT ); + return entityPropertyBinder + .bind( + getPropertyName(fk, targetTable, inverseProperty), + true, + targetTable, + fk, + value, + inverseProperty); + } + + private void addColumns(ForeignKey foreignKey, OneToOne oneToOne, Set processedColumns) { + for (Column fkcolumn : foreignKey.getColumns()) { + BinderUtils.checkColumnForMultipleBinding(fkcolumn); + oneToOne.addColumn(fkcolumn); + processedColumns.add(fkcolumn); + } + + } + + private String getPropertyName(ForeignKey foreignKey, Table table, boolean inverseProperty) { + if (inverseProperty) { + return getForeignKeyToInverseEntityName(foreignKey, table); + } else { + return getForeignKeyToTentityName(foreignKey, table); + } + } + + private String getForeignKeyToTentityName(ForeignKey foreignKey, Table table) { + return getRevengStrategy().foreignKeyToEntityName( + foreignKey.getName(), + TableIdentifier.create(foreignKey.getReferencedTable()), + foreignKey.getReferencedColumns(), + TableIdentifier.create(table), + foreignKey.getColumns(), + ForeignKeyUtils.isUniqueReference(foreignKey)); + } + + private String getForeignKeyToInverseEntityName(ForeignKey foreignKey, Table table) { + return getRevengStrategy().foreignKeyToInverseEntityName( + foreignKey.getName(), + TableIdentifier.create(foreignKey.getReferencedTable()), + foreignKey.getReferencedColumns(), + TableIdentifier.create(table), + foreignKey.getColumns(), + ForeignKeyUtils.isUniqueReference(foreignKey)); + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PrimaryKeyBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PrimaryKeyBinder.java new file mode 100644 index 000000000000..7ccbe6f40945 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PrimaryKeyBinder.java @@ -0,0 +1,339 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.MetaAttribute; +import org.hibernate.mapping.OneToOne; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.RootClass; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.core.RevengMetadataCollector; +import org.hibernate.tool.reveng.internal.core.binder.ForeignKeyUtils.ForeignKeyForColumns; +import org.hibernate.tool.reveng.internal.core.util.EnhancedBasicValue; +import org.hibernate.tool.reveng.internal.core.util.EnhancedComponent; +import org.hibernate.tool.reveng.internal.core.util.EnhancedValue; +import org.hibernate.tool.reveng.internal.core.util.RevengUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +class PrimaryKeyBinder extends AbstractBinder { + + private static final Logger LOGGER = Logger.getLogger(PrimaryKeyBinder.class.getName()); + + static PrimaryKeyBinder create(BinderContext binderContext) { + return new PrimaryKeyBinder(binderContext); + } + + private final BasicPropertyBinder basicPropertyBinder; + private final BasicValueBinder simpleValueBinder; + private final ManyToOneBinder manyToOneBinder; + private final PropertyBinder propertyBinder; + + + private PrimaryKeyBinder(BinderContext binderContext) { + super(binderContext); + this.basicPropertyBinder = BasicPropertyBinder.create(binderContext); + this.simpleValueBinder = BasicValueBinder.create(binderContext); + this.manyToOneBinder = ManyToOneBinder.create(binderContext); + this.propertyBinder = PropertyBinder.create(binderContext); + } + + PrimaryKeyInfo bind( + Table table, + RootClass rc, + Set processed, + RevengMetadataCollector revengMetadataCollector) { + List keyColumns = getKeyColumns(table); + final TableIdentifier tableIdentifier = TableIdentifier.create(table); + PrimaryKeyInfo pki = createPrimaryKeyInfo(tableIdentifier, keyColumns); + EnhancedValue id = createKeyValue(rc, keyColumns, pki.suggestedStrategy, table, revengMetadataCollector, processed); + id.setIdentifierGeneratorProperties(pki.suggestedProperties); + Property property = propertyBinder.bind( + table, + BinderUtils.makeUnique(rc,getIdPropertyName(tableIdentifier, keyColumns)), + id, + RevengUtils.createAssociationInfo(null, null, true, true)); + rc.setIdentifierProperty(property); + rc.setDeclaredIdentifierProperty(property); + rc.setIdentifier(id); + return pki; + } + + void updatePrimaryKey(RootClass rc, PrimaryKeyInfo pki) { + EnhancedValue idValue = (EnhancedValue) rc.getIdentifierProperty().getValue(); + Properties defaultStrategyProperties = new Properties(); + Property constrainedOneToOne = getConstrainedOneToOne(rc); + if(constrainedOneToOne!=null) { + if(pki.suggestedStrategy==null) { + idValue.setIdentifierGeneratorStrategy("foreign"); + } + if(pki.suggestedProperties==null) { + defaultStrategyProperties.setProperty("property", constrainedOneToOne.getName()); + idValue.setIdentifierGeneratorProperties(defaultStrategyProperties); + } + } + } + + private EnhancedValue createKeyValue( + PersistentClass rc, + List keyColumns, + String suggestedStrategyName, + Table table, + RevengMetadataCollector revengMetadataCollector, + Set processed) { + if (keyColumns.size()>1) { + LOGGER.log(Level.INFO, "id strategy for " + rc.getEntityName() + " since it has a multiple column primary key"); + return handleCompositeKey(rc, processed, keyColumns); + } + else { + String tableIdentifierStrategyName = + getTableIdentifierStrategyName(suggestedStrategyName, revengMetadataCollector, table); + return handleColumnKey(table, tableIdentifierStrategyName, processed, keyColumns); + } + } + + private PrimaryKeyInfo createPrimaryKeyInfo( + TableIdentifier tableIdentifier, + List keyColumns) { + PrimaryKeyInfo result = new PrimaryKeyInfo(); + result.suggestedProperties = getRevengStrategy().getTableIdentifierProperties(tableIdentifier); + if (keyColumns.size() == 1) { + result.suggestedStrategy = RevengUtils.getTableIdentifierStrategyNameInRevengStrategy( + getRevengStrategy(), + tableIdentifier, + getDefaultCatalog(), + getDefaultSchema()); + } + return result; + } + + private String getTableIdentifierStrategyName( + String suggestedStrategy, + RevengMetadataCollector revengMetadataCollector, + Table table) { + if(suggestedStrategy==null) { + suggestedStrategy = revengMetadataCollector.getSuggestedIdentifierStrategy( + table.getCatalog(), + table.getSchema(), + table.getName() ); + return suggestedStrategy == null ? "assigned" : suggestedStrategy; + } else { + return suggestedStrategy; + } + } + + private String getIdPropertyName( + TableIdentifier tableIdentifier, + + List keyColumns) { + String result = getRevengStrategy().tableToIdentifierPropertyName(tableIdentifier); + if (result == null) { + if (keyColumns.size() > 1) { + result = "id"; + } else { + result = getRevengStrategy().columnToPropertyName( + tableIdentifier, + keyColumns.get(0).getName()); + } + } + return result; + } + + private boolean isGeneratedId( + List keyColumns, + String tableIdentifierStrategyName) { + boolean result = false; + if (keyColumns.size() == 1) { + result = !"assigned".equals(tableIdentifierStrategyName); + } + return result; + } + + private List getKeyColumns(Table table) { + List result = null; + if (table.getPrimaryKey()!=null) { + result = table.getPrimaryKey().getColumns(); + } + else { + LOGGER.log(Level.INFO, "No primary key found for " + table + ", using all properties as the identifier."); + result = new ArrayList(); + for (Column col : table.getColumns()) { + result.add(col); + } + } + return result; + } + + private EnhancedBasicValue handleColumnKey( + Table table, + String tableIdentifierStrategyName, + Set processed, + List keyColumns) { + Column pkc = (Column) keyColumns.get(0); + BinderUtils.checkColumnForMultipleBinding(pkc); + processed.add(pkc); + EnhancedBasicValue result = simpleValueBinder.bind( + table, + pkc, + isGeneratedId(keyColumns, tableIdentifierStrategyName)); + result.setIdentifierGeneratorStrategy(tableIdentifierStrategyName); + return result; + } + + private EnhancedComponent handleCompositeKey( + PersistentClass rc, + Set processedColumns, + List keyColumns) { + EnhancedComponent result = new EnhancedComponent(getMetadataBuildingContext(), rc); + result.setMetaAttributes(Collections.emptyMap()); + result.setEmbedded(false); + result.setComponentClassName(getCompositeIdName(rc)); + addKeyColumns(result, rc.getTable(), getKeyColumns(rc.getTable(), keyColumns), processedColumns); + result.setNullValue("undefined"); + result.setIdentifierGeneratorStrategy("assigned"); + return result; + } + + private static void markAsUseInEquals(Property property) { + Map m = new HashMap(); + MetaAttribute ma = new MetaAttribute("use-in-equals"); + ma.addValue("true"); + m.put(ma.getName(),ma); + property.setMetaAttributes(m); + } + + private Property getConstrainedOneToOne(RootClass rc) { + for (Property property : rc.getProperties()) { + if(property.getValue() instanceof OneToOne) { + OneToOne oto = (OneToOne) property.getValue(); + if(oto.isConstrained()) { + return property; + } + } + } + return null; + } + + private Property bindManyToOneProperty( + Component pkc, + ForeignKeyForColumns fkfc, + Table table, Set + processedColumns) { + ForeignKey foreignKey = fkfc.key; + Property property = manyToOneBinder.bind( + BinderUtils.makeUnique(pkc, getForeignKeyToEntityName(foreignKey)), + true, + table, + foreignKey, + processedColumns); + processedColumns.addAll(fkfc.columns); + return property; + } + + private Property bindBasicProperty( + Component pkc, + Column column, + Table table, + Set processedColumns) { + Property result = null; + if ( processedColumns.contains(column) ) { + throw new RuntimeException("Binding column twice for primary key should not happen: " + column); + } + else { + BinderUtils.checkColumnForMultipleBinding(column); + result = basicPropertyBinder.bind( + BinderUtils.makeUnique(pkc, getColumnToPropertyName(table, column)), + table, + column); + processedColumns.add(column); + } + return result; + } + + private String getForeignKeyToEntityName(ForeignKey foreignKey) { + return getRevengStrategy().foreignKeyToEntityName( + foreignKey.getName(), + TableIdentifier.create(foreignKey.getTable()), + foreignKey.getColumns(), + TableIdentifier.create(foreignKey.getReferencedTable()), + foreignKey.getReferencedColumns(), + true + ); + } + + private String getColumnToPropertyName(Table table, Column column) { + return getRevengStrategy().columnToPropertyName( + TableIdentifier.create(table), + column.getName()); + } + + private void addKeyColumns( + Component pkc, + Table table, + List list, + Set processedColumns) { + for (Iterator iter = list.iterator(); iter.hasNext();) { + Object element = iter.next(); + Property property; + if (element instanceof Column) { + property = bindBasicProperty(pkc, (Column)element, table, processedColumns); + } + else if (element instanceof ForeignKeyForColumns) { + property = bindManyToOneProperty(pkc, (ForeignKeyForColumns)element, table, processedColumns); + } + else { + throw new RuntimeException("unknown thing"); + } + markAsUseInEquals(property); + pkc.addProperty(property); + } + } + + private List getKeyColumns(Table table, List keyColumns) { + if (preferBasicCompositeIds() ) { + return new ArrayList(keyColumns); + } + else { + return ForeignKeyUtils.findForeignKeys(table.getForeignKeys().values(), keyColumns); + } + } + + private String getCompositeIdName(PersistentClass pc) { + String compositeIdName = getRevengStrategy().tableToCompositeIdName( + TableIdentifier.create(pc.getTable())); + if(compositeIdName==null) { + compositeIdName = getRevengStrategy().classNameToCompositeIdName(pc.getClassName()); + } + return compositeIdName; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PrimaryKeyInfo.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PrimaryKeyInfo.java new file mode 100644 index 000000000000..52abec3cb1c3 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PrimaryKeyInfo.java @@ -0,0 +1,27 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import java.util.Properties; + +public class PrimaryKeyInfo { + + public String suggestedStrategy = null; + public Properties suggestedProperties = null; + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PropertyBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PropertyBinder.java new file mode 100644 index 000000000000..ed08b6d85f15 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/PropertyBinder.java @@ -0,0 +1,101 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.FetchMode; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Fetchable; +import org.hibernate.mapping.MetaAttribute; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Table; +import org.hibernate.mapping.Value; +import org.hibernate.tool.reveng.api.core.AssociationInfo; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.core.util.RevengUtils; + +import java.util.Map; + +class PropertyBinder extends AbstractBinder { + + static PropertyBinder create(BinderContext binderContext) { + return new PropertyBinder(binderContext); + } + + private PropertyBinder(BinderContext binderContext) { + super(binderContext); + } + + Property bind( + Table table, + String propertyName, + Value value, + AssociationInfo associationInfo) { + return bindMetaAttributes( + createProperty(propertyName, value, associationInfo), + table); + } + + private Property createProperty( + String propertyName, + Value value, + AssociationInfo associationInfo) { + Property result = new Property(); + result.setName(propertyName); + result.setValue(value); + result.setInsertable(associationInfo.getInsert()); + result.setUpdateable(associationInfo.getUpdate()); + String cascade = associationInfo.getCascade(); + cascade = cascade == null ? "none" : cascade; + result.setCascade(cascade); + boolean lazy = false; + if (Fetchable.class.isInstance(value)) { + lazy = ((Fetchable)value).getFetchMode() != FetchMode.JOIN; + } + result.setLazy(lazy); + result.setPropertyAccessorName("property"); + return result; + } + + private Property bindMetaAttributes(Property property, Table table) { + for (Column col : property.getColumns()) { + Map map = getColumnToMetaAttributesInRevengStrategy(table, col.getName()); + if(map!=null) { + property.setMetaAttributes(map); + } + } + + return property; + } + + private Map getColumnToMetaAttributesInRevengStrategy( + Table table, + String column) { + Map result = null; + TableIdentifier tableIdentifier = TableIdentifier.create(table); + result = getRevengStrategy().columnToMetaAttributes(tableIdentifier, column); + if (result == null) { + tableIdentifier = RevengUtils.createTableIdentifier( + table, + getDefaultCatalog(), + getDefaultSchema()); + result = getRevengStrategy().columnToMetaAttributes(tableIdentifier, column); + } + return result; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/RootClassBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/RootClassBinder.java new file mode 100644 index 000000000000..0d105f0f3f0f --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/RootClassBinder.java @@ -0,0 +1,215 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.DuplicateMappingException; +import org.hibernate.engine.OptimisticLockStyle; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.MetaAttribute; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.RootClass; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.core.RevengMetadataCollector; +import org.hibernate.tool.reveng.internal.core.util.RevengUtils; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class RootClassBinder extends AbstractBinder { + + private static final Logger LOGGER = Logger.getLogger(RootClassBinder.class.getName()); + + public static RootClassBinder create( + BinderContext binderContext) { + return new RootClassBinder(binderContext); + } + + private final PrimaryKeyBinder primaryKeyBinder; + private final VersionPropertyBinder versionPropertyBinder; + private final ForeignKeyBinder foreignKeyBinder; + private final BasicPropertyBinder basicPropertyBinder; + + private RootClassBinder(BinderContext binderContext) { + super(binderContext); + this.primaryKeyBinder = PrimaryKeyBinder.create(binderContext); + this.versionPropertyBinder = VersionPropertyBinder.create(binderContext); + this.foreignKeyBinder = ForeignKeyBinder.create(binderContext); + this.basicPropertyBinder = BasicPropertyBinder.create(binderContext); + } + + public void bind(Table table, RevengMetadataCollector revengMetadataCollector) { + Set processed = new HashSet(); + nullifyDefaultCatalogAndSchema(table); + RootClass rc = createRootClass(table); + addToMetadataCollector(rc, table); + PrimaryKeyInfo pki = bindPrimaryKey(table, rc, processed, revengMetadataCollector); + bindVersionProperty(table, rc, processed); + bindOutgoingForeignKeys(table, rc, processed); + bindColumnsToProperties(table, rc, processed); + bindIncomingForeignKeys(rc, processed, revengMetadataCollector); + updatePrimaryKey(rc, pki); + } + + private PrimaryKeyInfo bindPrimaryKey( + Table table, + RootClass rc, + Set processed, + RevengMetadataCollector revengMetadataCollector) { + return primaryKeyBinder.bind(table, rc, processed, revengMetadataCollector); + } + + private void updatePrimaryKey(RootClass rc, PrimaryKeyInfo pki) { + primaryKeyBinder.updatePrimaryKey(rc, pki); + } + + private void addToMetadataCollector(RootClass rc, Table table) { + try { + getMetadataCollector().addEntityBinding(rc); + getMetadataCollector().addImport( rc.getEntityName(), rc.getEntityName() ); + } catch(DuplicateMappingException dme) { + // TODO: detect this and generate a "permutation" of it ? + PersistentClass class1 = getMetadataCollector().getEntityBinding(dme.getName()); + Table table2 = class1.getTable(); + throw new RuntimeException("Duplicate class name '" + rc.getEntityName() + "' generated for '" + table + "'. Same name where generated for '" + table2 + "'"); + } + } + + private RootClass createRootClass(Table table) { + RootClass rc = new RootClass(getMetadataBuildingContext()); + TableIdentifier tableIdentifier = TableIdentifier.create(table); + String className = getRevengStrategy().tableToClassName( tableIdentifier ); + LOGGER.log(Level.INFO, "Building entity " + className + " based on " + tableIdentifier); + rc.setEntityName( className ); + rc.setJpaEntityName( StringHelper.unqualify( className ) ); + rc.setClassName( className ); + rc.setProxyInterfaceName( rc.getEntityName() ); // TODO: configurable ? + rc.setLazy(true); + rc.setMetaAttributes(getMetaAttributes(table)); + rc.setDiscriminatorValue( rc.getEntityName() ); + rc.setTable(table); + rc.setOptimisticLockStyle(OptimisticLockStyle.NONE); + return rc; + } + + private void nullifyDefaultCatalogAndSchema(Table table) { + if (table.getCatalog() != null && table.getCatalog().equals(getDefaultCatalog())) { + table.setCatalog(null); + } + if (table.getSchema() != null && table.getSchema().equals(getDefaultSchema())) { + table.setSchema(null); + } + } + + private void bindVersionProperty( + Table table, + RootClass rc, + Set processed) { + versionPropertyBinder.bind(table, rc, processed); + } + + private void bindIncomingForeignKeys( + PersistentClass rc, + Set processed, + RevengMetadataCollector revengMetadataCollector) { + List foreignKeys = revengMetadataCollector.getOneToManyCandidates().get(rc.getEntityName()); + if(foreignKeys!=null) { + for (Iterator iter = foreignKeys.iterator(); iter.hasNext();) { + foreignKeyBinder.bindIncoming(iter.next(), rc, processed); + } + } + } + + + private void bindOutgoingForeignKeys(Table table, RootClass rc, Set processedColumns) { + // Iterate the outgoing foreign keys and create many-to-one's + for (ForeignKey foreignKey : table.getForeignKeys().values()) { + boolean mutable = true; + if ( contains( foreignKey.getColumns().iterator(), processedColumns ) ) { + if ( !preferBasicCompositeIds() ) continue; //it's in the pk, so skip this one + mutable = false; + } + foreignKeyBinder.bindOutgoing(foreignKey, table, rc, processedColumns, mutable); + } + } + + private void bindColumnsToProperties(Table table, RootClass rc, Set processedColumns) { + for (Column column : table.getColumns()) { + if ( !processedColumns.contains(column) ) { + BinderUtils.checkColumnForMultipleBinding(column); + String propertyName = getColumnToPropertyNameInRevengStrategy(table, column); + Property property = basicPropertyBinder.bind( + BinderUtils.makeUnique(rc,propertyName), + table, + column); + rc.addProperty(property); + } + } + } + + private boolean contains(Iterator columnIterator, Set processedColumns) { + while (columnIterator.hasNext() ) { + Column element = (Column) columnIterator.next(); + if(processedColumns.contains(element) ) { + return true; + } + } + return false; + } + + private Map getMetaAttributes(Table table) { + Map result = null; + TableIdentifier tableIdentifier = TableIdentifier.create(table); + result = getRevengStrategy().tableToMetaAttributes(tableIdentifier); + if (result == null) { + tableIdentifier = RevengUtils.createTableIdentifier( + table, + getDefaultCatalog(), + getDefaultSchema()); + result = getRevengStrategy().tableToMetaAttributes(tableIdentifier); + } + if (result == null) { + result = Collections.emptyMap(); + } + return result; + } + + private String getColumnToPropertyNameInRevengStrategy( + Table table, + Column column) { + String result = null; + String columnName = column.getName(); + TableIdentifier tableIdentifier = TableIdentifier.create(table); + result = getRevengStrategy().columnToPropertyName(tableIdentifier, columnName); + if (result == null) { + tableIdentifier = RevengUtils.createTableIdentifier(table, getDefaultCatalog(), getDefaultSchema()); + result = getRevengStrategy().columnToPropertyName(tableIdentifier, columnName); + } + return result; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/TypeUtils.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/TypeUtils.java new file mode 100644 index 000000000000..633d7073ed93 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/TypeUtils.java @@ -0,0 +1,131 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.boot.spi.InFlightMetadataCollector; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.util.JdbcToHibernateTypeHelper; +import org.hibernate.tool.reveng.internal.util.TableNameQualifier; +import org.hibernate.type.Type; + +import java.lang.reflect.Field; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class TypeUtils { + + private static final Logger LOGGER = Logger.getLogger(TypeUtils.class.getName()); + + public static final int DEFAULT_COLUMN_LENGTH = 255; + public static final int DEFAULT_COLUMN_PRECISION = 19; + public static final int DEFAULT_COLUMN_SCALE = 2; + + public static String determinePreferredType( + InFlightMetadataCollector metadataCollector, + RevengStrategy revengStrategy, + Table table, + Column column, + boolean generatedIdentifier) { + + String location = + "Table: " + + TableNameQualifier.qualify( + table.getCatalog(), + table.getSchema(), + table.getQuotedName() ) + + " column: " + + column.getQuotedName(); + + Integer sqlTypeCode = column.getSqlTypeCode(); + if(sqlTypeCode==null) { + throw new RuntimeException("sqltype is null for " + location); + } + + String preferredHibernateType = revengStrategy.columnToHibernateTypeName( + TableIdentifier.create(table), + column.getName(), + sqlTypeCode.intValue(), + column.getLength() != null ? column.getLength().intValue() : DEFAULT_COLUMN_LENGTH, + column.getPrecision() != null ? column.getPrecision().intValue() : DEFAULT_COLUMN_PRECISION, + column.getScale() != null ? column.getScale().intValue() : DEFAULT_COLUMN_SCALE, + column.isNullable(), + generatedIdentifier + ); + + Type wantedType = metadataCollector + .getTypeConfiguration() + .getBasicTypeRegistry() + .getRegisteredType(preferredHibernateType); + + if(wantedType!=null) { + + int[] wantedSqlTypes = wantedType.getSqlTypeCodes(metadataCollector); + + if(wantedSqlTypes.length>1) { + throw new RuntimeException("The type " + preferredHibernateType + " found on " + location + " spans multiple columns. Only single column types allowed."); + } + + int wantedSqlType = wantedSqlTypes[0]; + if(wantedSqlType!=sqlTypeCode.intValue() ) { + LOGGER.log( + Level.INFO, + "Sql type mismatch for " + location + " between DB and wanted hibernate type. Sql type set to " + typeCodeName( sqlTypeCode.intValue() ) + " instead of " + typeCodeName(wantedSqlType) ); + forceSqlTypeCode(column, wantedSqlType); + } + + } + + else { + + LOGGER.log( + Level.INFO, + "No Hibernate type found for " + preferredHibernateType + ". Most likely cause is a missing UserType class."); + + } + + + + if(preferredHibernateType==null) { + throw new RuntimeException("Could not find javatype for " + typeCodeName(sqlTypeCode.intValue())); + } + + return preferredHibernateType; + } + + private static String typeCodeName(int sqlTypeCode) { + return sqlTypeCode + "(" + JdbcToHibernateTypeHelper.getJDBCTypeName(sqlTypeCode) + ")"; + } + + private static void forceSqlTypeCode(Column column, int sqlCode) { + try { + Field sqlCodeField = Column.class.getDeclaredField("sqlTypeCode"); + sqlCodeField.setAccessible(true); + sqlCodeField.set(column, Integer.valueOf(sqlCode)); + } catch (NoSuchFieldException | + SecurityException | + IllegalArgumentException | + IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/VersionPropertyBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/VersionPropertyBinder.java new file mode 100644 index 000000000000..7aba43335de9 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/binder/VersionPropertyBinder.java @@ -0,0 +1,107 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +import org.hibernate.engine.OptimisticLockStyle; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.RootClass; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.TableIdentifier; + +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +class VersionPropertyBinder extends AbstractBinder { + + private final static Logger LOGGER = Logger.getLogger(VersionPropertyBinder.class.getName()); + + static VersionPropertyBinder create(BinderContext binderContext) { + return new VersionPropertyBinder(binderContext); + } + + private final BasicPropertyBinder basicPropertyBinder; + + private VersionPropertyBinder(BinderContext binderContext) { + super(binderContext); + this.basicPropertyBinder = BasicPropertyBinder.create(binderContext); + } + + void bind( + Table table, + RootClass rc, + Set processed) { + String optimisticLockColumnName = getRevengStrategy() + .getOptimisticLockColumnName(TableIdentifier.create(table)); + if(optimisticLockColumnName!=null) { + handleSpecifiedVersionColumn(table, optimisticLockColumnName, rc, processed); + } else { + scanForAppropriateVersionColumn(table, rc, processed); + } + } + + private void scanForAppropriateVersionColumn( + Table table, + RootClass rc, + Set processed) { + TableIdentifier identifier = TableIdentifier.create(table); + LOGGER.log(Level.INFO, "Scanning " + identifier + " for / columns."); + for (Column column : table.getColumns()) { + boolean useIt = getRevengStrategy().useColumnForOptimisticLock(identifier, column.getName()); + if(useIt && !processed.contains(column)) { + bindVersionProperty(table, column, rc, processed); + return; + } + } + LOGGER.log(Level.INFO, "No columns reported while scanning for / columns in " + identifier); + } + + private void handleSpecifiedVersionColumn( + Table table, + String optimisticLockColumnName, + RootClass rc, + Set processed) { + TableIdentifier identifier = TableIdentifier.create(table); + Column column = table.getColumn(new Column(optimisticLockColumnName)); + if(column==null) { + LOGGER.log(Level.WARNING, "Column " + column + " wanted for / not found in " + identifier); + } else { + bindVersionProperty(table, column, rc, processed); + } + } + + private void bindVersionProperty( + Table table, + Column column, + RootClass rc, + Set processed) { + TableIdentifier identifier = TableIdentifier.create(table); + processed.add(column); + String propertyName = getRevengStrategy().columnToPropertyName( identifier, column.getName() ); + Property property = basicPropertyBinder.bind( + BinderUtils.makeUnique(rc, propertyName), + table, + column); + rc.addProperty(property); + rc.setVersion(property); + rc.setOptimisticLockStyle(OptimisticLockStyle.VERSION); + LOGGER.log(Level.INFO, "Column " + column.getName() + " will be used for / columns in " + identifier); + + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/AbstractMetaDataDialect.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/AbstractMetaDataDialect.java new file mode 100644 index 000000000000..7f730c3f4993 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/AbstractMetaDataDialect.java @@ -0,0 +1,201 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.dialect; + +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.tool.reveng.api.core.RevengDialect; +import org.jboss.logging.Logger; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * abstract base class for the metadatadialects to hold the + * basic setup classes. + * + * @author max + * + */ +public abstract class AbstractMetaDataDialect implements RevengDialect { + + protected final Logger log = Logger.getLogger(this.getClass()); + + private Connection connection; + private DatabaseMetaData metaData; + + private ConnectionProvider connectionProvider = null; + + public void configure( + ConnectionProvider connectionProvider) { + this.connectionProvider = connectionProvider; + } + + public void close() { + metaData = null; + if(connection != null) { + try { + connectionProvider.closeConnection(connection); + } + catch (SQLException e) { + throw new RuntimeException("Problem while closing connection", e); + } finally { + connection = null; + } + } + connectionProvider = null; + } + + protected DatabaseMetaData getMetaData() { + if (metaData == null) { + try { + metaData = getConnection().getMetaData(); + } + catch (SQLException e) { + throw new RuntimeException("Getting database metadata", e); + } + } + return metaData; + } + + protected String getDatabaseStructure(String catalog, String schema) { + ResultSet schemaRs = null; + ResultSet catalogRs = null; + String nl = System.lineSeparator(); + StringBuilder sb = new StringBuilder(nl); + // Let's give the user some feedback. The exception + // is probably related to incorrect schema configuration. + sb.append("Configured schema:").append(schema).append(nl); + sb.append("Configured catalog:").append(catalog ).append(nl); + + try { + schemaRs = getMetaData().getSchemas(); + sb.append("Available schemas:").append(nl); + while (schemaRs.next() ) { + sb.append(" ").append(schemaRs.getString("TABLE_SCHEM") ).append(nl); + } + } + catch (SQLException e2) { + log.warn("Could not get schemas", e2); + sb.append(" ").append(nl); + } + finally { + try { + if (schemaRs != null) { + schemaRs.close(); + } + } + catch (Exception ignore) { + } + } + + try { + catalogRs = getMetaData().getCatalogs(); + sb.append("Available catalogs:").append(nl); + while (catalogRs.next() ) { + sb.append(" ").append(catalogRs.getString("TABLE_CAT") ).append(nl); + } + } + catch (SQLException e2) { + log.warn("Could not get catalogs", e2); + sb.append(" ").append(nl); + } + finally { + try { + if (catalogRs != null) { + catalogRs.close(); + } + } + catch (Exception ignore) { + } + } + return sb.toString(); + } + + protected Connection getConnection() throws SQLException { + if(connection==null) { + connection = connectionProvider.getConnection(); + } + return connection; + } + + public void close(Iterator iterator) { + if(iterator instanceof ResultSetIterator) { + ((ResultSetIterator)iterator).close(); + } + } + + public boolean needQuote(String name) { + + if(name==null) return false; + + // TODO: use jdbc metadata to decide on this. but for now we just handle the most typical cases. + if(name.indexOf('-')>0) return true; + if(name.indexOf(' ')>0) return true; + return name.indexOf( '.' ) > 0; + } + + protected String caseForSearch(String value) throws SQLException { + // TODO: handle quoted requests (just strip it ?) + if(needQuote(value)) { + if ( getMetaData().storesMixedCaseQuotedIdentifiers() ) { + return value; + } else if ( getMetaData().storesUpperCaseQuotedIdentifiers() ) { + return toUpperCase( value ); + } else if( getMetaData().storesLowerCaseQuotedIdentifiers() ) { + return toLowerCase( value ); + } else { + return value; + } + } else if ( getMetaData().storesMixedCaseQuotedIdentifiers() ) { + return value; + } else if ( getMetaData().storesUpperCaseIdentifiers() ) { + return toUpperCase( value ); + } else if( getMetaData().storesLowerCaseIdentifiers() ) { + return toLowerCase( value ); + } else { + return value; + } + } + + private String toUpperCase(String str) { + return str==null ? null : str.toUpperCase(); + } + + private String toLowerCase(String str) { + return str == null ? null : str.toLowerCase(Locale.ENGLISH); + } + + public Iterator> getSuggestedPrimaryKeyStrategyName(String catalog, String schema, String table) { + Map m = new HashMap(); + m.put( "TABLE_CAT", catalog ); + m.put( "TABLE_SCHEMA", schema ); + m.put( "TABLE_NAME", table ); + m.put( "HIBERNATE_STRATEGY", null ); + List> l = new ArrayList>(); + l.add(m); + return l.iterator(); + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/CachedMetaDataDialect.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/CachedMetaDataDialect.java new file mode 100644 index 000000000000..2d314f653bee --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/CachedMetaDataDialect.java @@ -0,0 +1,224 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.dialect; + +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.tool.reveng.api.core.RevengDialect; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class CachedMetaDataDialect implements RevengDialect { + + RevengDialect delegate; + private final Map>> cachedTables = new HashMap>>(); + private final Map>> cachedColumns = new HashMap>>(); + private final Map>> cachedExportedKeys = new HashMap>>(); + private final Map>> cachedPrimaryKeys = new HashMap>>(); + private final Map>> cachedIndexInfo = new HashMap>>(); + private final Map>> cachedPrimaryKeyStrategyName = new HashMap>>(); + + public CachedMetaDataDialect(RevengDialect realMetaData) { + this.delegate = realMetaData; + } + + public void close() { + delegate.close(); + } + + public void configure( + ConnectionProvider connectionProvider) { + delegate.configure(connectionProvider); + } + + public void close(Iterator iterator) { + if( iterator instanceof CachedIterator ci ) { + if(ci.getOwner()==this) { + ci.store(); + return; + } + } + delegate.close( iterator ); + } + + + + public Iterator> getColumns(String catalog, String schema, String table, String column) { + StringKey sk = new StringKey(new String[] { catalog, schema, table, column }); + List> cached = cachedColumns.get( sk ); + if(cached==null) { + cached = new ArrayList>(); + return new CachedIterator(this, cachedColumns, sk, cached, delegate.getColumns( catalog, schema, table, column )); + } else { + return cached.iterator(); + } + } + + public Iterator> getExportedKeys(String catalog, String schema, String table) { + StringKey sk = new StringKey(new String[] { catalog, schema, table }); + List> cached = cachedExportedKeys.get( sk ); + if(cached==null) { + cached = new ArrayList>(); + return new CachedIterator(this, cachedExportedKeys, sk, cached, delegate.getExportedKeys( catalog, schema, table )); + } else { + return cached.iterator(); + } + } + + public Iterator> getIndexInfo(String catalog, String schema, String table) { + StringKey sk = new StringKey(new String[] { catalog, schema, table }); + List> cached = cachedIndexInfo.get( sk ); + if(cached==null) { + cached = new ArrayList>(); + return new CachedIterator(this, cachedIndexInfo, sk, cached, delegate.getIndexInfo( catalog, schema, table )); + } else { + return cached.iterator(); + } + } + + public Iterator> getPrimaryKeys(String catalog, String schema, String name) { + StringKey sk = new StringKey(new String[] { catalog, schema, name }); + List> cached = cachedPrimaryKeys .get( sk ); + if(cached==null) { + cached = new ArrayList>(); + return new CachedIterator(this, cachedPrimaryKeys, sk, cached, delegate.getPrimaryKeys( catalog, schema, name )); + } else { + return cached.iterator(); + } + } + + public Iterator> getTables(String catalog, String schema, String table) { + StringKey sk = new StringKey(new String[] { catalog, schema, table }); + List> cached = cachedTables.get( sk ); + if(cached==null) { + cached = new ArrayList>(); + return new CachedIterator(this, cachedTables, sk, cached, delegate.getTables( catalog, schema, table )); + } else { + return cached.iterator(); + } + } + + public Iterator> getSuggestedPrimaryKeyStrategyName(String catalog, String schema, String table) { + StringKey sk = new StringKey(new String[] { catalog, schema, table }); + List> cached = cachedPrimaryKeyStrategyName.get( sk ); + if(cached==null) { + cached = new ArrayList>(); + return new CachedIterator(this, cachedPrimaryKeyStrategyName, sk, cached, delegate.getSuggestedPrimaryKeyStrategyName( catalog, schema, table )); + } else { + return cached.iterator(); + } + } + + public boolean needQuote(String name) { + return delegate.needQuote( name ); + } + + private static class StringKey { + String[] keys; + + StringKey(String[] key) { + this.keys=key; + } + + public int hashCode() { + if (keys == null) + return 0; + + int result = 1; + + for ( Object element : keys ) { + result = 31 * result + (element == null ? 0 : element.hashCode()); + } + + return result; + } + + public boolean equals(Object obj) { + if (!(obj instanceof StringKey other)) return false; + String[] otherKeys = other.keys; + if(otherKeys.length!=keys.length) { + return false; + } + + for (int i = otherKeys.length-1; i >= 0; i--) { + if(!safeEquals(otherKeys[i],(keys[i]))) { + return false; + } + } + + return true; + } + + private boolean safeEquals(Object obj1, Object obj2) { + if ( obj1 == null ) { + return obj2 == null; + } + return obj1.equals( obj2 ); + } + } + + private static class CachedIterator implements Iterator> { + + private List> cache; + private StringKey target; + private Map>> destination; + private Iterator> realIterator; + final CachedMetaDataDialect owner; + public CachedIterator(CachedMetaDataDialect owner, Map>> destination, StringKey sk, List> cache, Iterator> realIterator) { + this.owner = owner; + this.destination = destination; + this.target = sk; + this.realIterator = realIterator; + this.cache = cache; + } + + public CachedMetaDataDialect getOwner() { + return owner; + } + + public boolean hasNext() { + return realIterator.hasNext(); + } + + public Map next() { + Map map = realIterator.next(); + cache.add(new HashMap(map)); // need to copy since MetaDataDialect might reuse it. + return map; + } + + public void remove() { + realIterator.remove(); + } + + public void store() { + destination.put( target, cache ); + if(realIterator.hasNext()) throw new IllegalStateException("CachedMetaDataDialect have not been fully initialized!"); + cache = null; + target = null; + destination = null; + realIterator = null; + } + } + + + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/H2MetaDataDialect.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/H2MetaDataDialect.java new file mode 100644 index 000000000000..31877f0eafbb --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/H2MetaDataDialect.java @@ -0,0 +1,154 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.dialect; + +import org.hibernate.tool.reveng.internal.util.ReflectionUtil; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + + +/** + * MetaData dialect that work around tweaks in the H2 database. + * + * @author Max Rydahl Andersen + * + */ +public class H2MetaDataDialect extends JDBCMetaDataDialect { + + private static final String SPKSQ_H2_1_X = + "SELECT " + + " idx.TABLE_CATALOG TABLE_CAT, " + + " idx.TABLE_SCHEMA TABLE_SCHEM, " + + " idx.TABLE_NAME, " + + " idx.COLUMN_NAME " + + "FROM " + + " INFORMATION_SCHEMA.INDEXES idx, " + + " INFORMATION_SCHEMA.COLUMNS cols " + + "WHERE " + + " idx.TABLE_CATALOG = cols.TABLE_CATALOG AND " + + " idx.TABLE_SCHEMA = cols.TABLE_SCHEMA AND " + + " idx.TABLE_NAME = cols.TABLE_NAME AND " + + " idx.PRIMARY_KEY = TRUE AND " + + " cols.COLUMN_DEFAULT like '%NEXT VALUE FOR%' "; + + private static final String SPKSQ_H2_2_X = + "SELECT " + + " idx.TABLE_CATALOG TABLE_CAT, " + + " idx.TABLE_SCHEMA TABLE_SCHEM, " + + " idx.TABLE_NAME, " + + " cols.COLUMN_NAME " + + "FROM " + + " INFORMATION_SCHEMA.INDEXES idx, " + + " INFORMATION_SCHEMA.INDEX_COLUMNS idx_cols, " + + " INFORMATION_SCHEMA.COLUMNS cols " + + "WHERE " + + " idx.TABLE_CATALOG = cols.TABLE_CATALOG AND " + + " idx.TABLE_SCHEMA = cols.TABLE_SCHEMA AND " + + " idx.TABLE_NAME = cols.TABLE_NAME AND " + + " idx.INDEX_TYPE_NAME = 'PRIMARY KEY' AND " + + " cols.COLUMN_NAME = idx_cols.COLUMN_NAME AND " + + " cols.IS_IDENTITY = 'YES'"; + + private static boolean understandsCatalogName = true; + + private String suggested_primary_key_strategy_query = null; + + public H2MetaDataDialect() { + super(); + try { + Class constants = ReflectionUtil.classForName( "org.h2.engine.Constants" ); + Integer build = (Integer)constants.getDeclaredField( "BUILD_ID" ).get( null ); + if ( build.intValue() < 55 ) { + understandsCatalogName = false; + } + suggested_primary_key_strategy_query = build.intValue() > 200 ? SPKSQ_H2_2_X : SPKSQ_H2_1_X; + } + catch( Throwable e ) { + // ignore (probably H2 not in the classpath) + } + } + + protected void putTableType(Map element, ResultSet tableRs) throws SQLException { + String tableType = tableRs.getString("TABLE_TYPE"); + if ("BASE TABLE".equals(tableType)) { + tableType = "TABLE"; + } + element.put("TABLE_TYPE", tableType); + } + + protected void putTablePart(Map element, ResultSet tableRs) throws SQLException { + super.putTablePart( element, tableRs ); + if ( !understandsCatalogName ) { + element.put( "TABLE_CAT", null ); + } + } + + protected void putExportedKeysPart(Map element, ResultSet rs) throws SQLException { + super.putExportedKeysPart( element, rs ); + if ( !understandsCatalogName ) { + element.put( "PKTABLE_CAT", null ); + } + } + + public Iterator> getSuggestedPrimaryKeyStrategyName(String catalog, String schema, String table) { + try { + catalog = caseForSearch( catalog ); + schema = caseForSearch( schema ); + table = caseForSearch( table ); + + log.debug("geSuggestedPrimaryKeyStrategyName(" + catalog + "." + schema + "." + table + ")"); + + String sql = suggested_primary_key_strategy_query; + if(catalog!=null) { + sql += "AND idx.TABLE_CATALOG like '" + catalog + "' "; + } + if(schema!=null) { + sql += "AND idx.TABLE_SCHEMA like '" + schema + "' "; + } + if(table!=null) { + sql += "AND idx.TABLE_NAME like '" + table + "' "; + } + + PreparedStatement statement = getConnection().prepareStatement( sql ); + + return new ResultSetIterator(statement.executeQuery()) { + + Map element = new HashMap(); + protected Map convertRow(ResultSet tableRs) throws SQLException { + element.clear(); + putTablePart( element, tableRs ); + element.put("HIBERNATE_STRATEGY", "identity"); + return element; + } + protected Throwable handleSQLException(SQLException e) { + // schemaRs and catalogRs are only used for error reporting if + // we get an exception + throw new RuntimeException( + "Could not get list of suggested identity strategies from database. Probably a JDBC driver problem. ", e ); + } + }; + } catch (SQLException e) { + throw new RuntimeException("Could not get list of suggested identity strategies from database. Probably a JDBC driver problem.", e); + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/HSQLMetaDataDialect.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/HSQLMetaDataDialect.java new file mode 100644 index 000000000000..12088d3fa41e --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/HSQLMetaDataDialect.java @@ -0,0 +1,116 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.dialect; + +import org.hibernate.tool.reveng.internal.util.TableNameQualifier; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * @author Dmitry Geraskov + * + */ +public class HSQLMetaDataDialect extends JDBCMetaDataDialect { + + private String quote(String columnName) { + if(columnName==null) return columnName; + if(needQuote(columnName)) { + if(columnName.length()>1 && columnName.charAt(0)=='\"' && columnName.charAt(columnName.length()-1)=='\"') { + return columnName; // avoid double quoting + } + return "\"" + columnName + "\""; + } else { + return columnName; + } + } + + public Iterator> getSuggestedPrimaryKeyStrategyName(String catalog, String schema, String table) { + try { + catalog = caseForSearch( catalog ); + schema = caseForSearch( schema ); + table = caseForSearch( table ); + + //log.debug("geSuggestedPrimaryKeyStrategyName(" + catalog + "." + schema + "." + table + ")"); + + final String sc = schema; + final String cat = catalog; + return new ResultSetIterator(getMetaData().getTables(catalog, schema, table, new String[]{"TABLE"})) { + + Map element = new HashMap(); + protected Map convertRow(ResultSet tableRs) throws SQLException{ + String table = tableRs.getString("TABLE_NAME"); + String fullTableName = TableNameQualifier.qualify(quote(cat), quote(sc), quote(table)); + + String sql ="SELECT * FROM " + fullTableName + " WHERE 0>1"; // can't use FALSE constant since it would not work with older HSQL versions. (JBIDE-5957) + boolean isAutoIncrement = false; + + PreparedStatement statement = null; + try { + statement = getConnection().prepareStatement( sql ); + element.clear(); + element.put("TABLE_NAME", table); + element.put("TABLE_SCHEM", sc); + element.put("TABLE_CAT", null); + + ResultSet rs = statement.executeQuery(); + ResultSetMetaData rsmd = rs.getMetaData(); + for (int i = 0; i < rsmd.getColumnCount(); i++) { + isAutoIncrement = rsmd.isAutoIncrement(i + 1); + if (isAutoIncrement) break; + } + + } catch(SQLException e) { + //log error and set HIBERNATE_STRATEGY to null + log.debug("Error while getting suggested primary key strategy for " + fullTableName + ". Falling back to default strategy.",e); + } finally { + if(statement!=null) { + try { + statement.close(); + } + catch (SQLException e) { + throw new RuntimeException( + "Problem while closing prepared statement", e); + } + } + } + + if(isAutoIncrement) { + element.put("HIBERNATE_STRATEGY", "identity"); + } else { + element.put("HIBERNATE_STRATEGY", null); + } + return element; + } + protected Throwable handleSQLException(SQLException e) { + // schemaRs and catalogRs are only used for error reporting if + // we get an exception + throw new RuntimeException( + "Could not get list of suggested identity strategies from database. Probably a JDBC driver problem. ", e); + } + }; + } catch (SQLException e) { + throw new RuntimeException("Could not get list of suggested identity strategies from database. Probably a JDBC driver problem. ", e); + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/JDBCMetaDataDialect.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/JDBCMetaDataDialect.java new file mode 100644 index 000000000000..ed797e535c30 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/JDBCMetaDataDialect.java @@ -0,0 +1,224 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.dialect; + +import org.hibernate.tool.reveng.internal.util.TableNameQualifier; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * MetaData dialect that uses standard JDBC for reading metadata. + * + * @author Max Rydahl Andersen + * + */ +public class JDBCMetaDataDialect extends AbstractMetaDataDialect { + + public Iterator> getTables(String xcatalog, String xschema, String xtable) { + try { + final String catalog = caseForSearch( xcatalog ); + final String schema = caseForSearch( xschema ); + final String table = caseForSearch( xtable ); + + log.debug("getTables(" + catalog + "." + schema + "." + table + ")"); + + ResultSet tableRs = getMetaData().getTables(catalog , schema , table, new String[] { "TABLE", "VIEW" }); + + return new ResultSetIterator(tableRs) { + + Map element = new HashMap(); + protected Map convertRow(ResultSet tableResultSet) throws SQLException { + element.clear(); + putTablePart( element, tableResultSet ); + putTableType(element, tableResultSet); + element.put("REMARKS", tableResultSet.getString("REMARKS")); + return element; + } + protected Throwable handleSQLException(SQLException e) { + // schemaRs and catalogRs are only used for error reporting if + // we get an exception + String databaseStructure = getDatabaseStructure( catalog, schema ); + throw new RuntimeException( + "Could not get list of tables from database. Probably a JDBC driver problem. " + + databaseStructure, + e ); + } + }; + } catch (SQLException e) { + // schemaRs and catalogRs are only used for error reporting if we get an exception + String databaseStructure = getDatabaseStructure(xcatalog,xschema); + throw new RuntimeException( + "Could not get list of tables from database. Probably a JDBC driver problem. " + databaseStructure, e); + } + } + + public Iterator> getIndexInfo(final String xcatalog, final String xschema, final String xtable) { + try { + final String catalog = caseForSearch( xcatalog ); + final String schema = caseForSearch( xschema ); + final String table = caseForSearch( xtable ); + + log.debug("getIndexInfo(" + catalog + "." + schema + "." + table + ")"); + ResultSet tableRs = getMetaData().getIndexInfo(catalog , schema , table, false, true); + + return new ResultSetIterator(tableRs) { + + Map element = new HashMap(); + protected Map convertRow(ResultSet rs) throws SQLException { + element.clear(); + putTablePart(element, rs); + element.put("INDEX_NAME", rs.getString("INDEX_NAME")); + element.put("COLUMN_NAME", rs.getString("COLUMN_NAME")); + element.put("NON_UNIQUE", Boolean.valueOf(rs.getBoolean("NON_UNIQUE"))); + element.put("TYPE", Short.valueOf(rs.getShort("TYPE"))); + return element; + } + protected Throwable handleSQLException(SQLException e) { + throw new RuntimeException( + "Exception while getting index info for " + TableNameQualifier.qualify(catalog, schema, table), e); + } + }; + } catch (SQLException e) { + throw new RuntimeException( + "Exception while getting index info for " + TableNameQualifier.qualify(xcatalog, xschema, xtable), e); + } + } + + protected void putTableType(Map element, ResultSet tableRs) throws SQLException { + element.put("TABLE_TYPE", tableRs.getString("TABLE_TYPE")); + } + + protected void putTablePart(Map element, ResultSet tableRs) throws SQLException { + element.put("TABLE_NAME", tableRs.getString("TABLE_NAME")); + element.put("TABLE_SCHEM", tableRs.getString("TABLE_SCHEM")); + element.put("TABLE_CAT", tableRs.getString("TABLE_CAT")); + } + + public Iterator> getColumns(final String xcatalog, final String xschema, final String xtable, String xcolumn) { + try { + final String catalog = caseForSearch( xcatalog ); + final String schema = caseForSearch( xschema ); + final String table = caseForSearch( xtable ); + final String column = caseForSearch( xcolumn ); + + log.debug("getColumns(" + catalog + "." + schema + "." + table + "." + column + ")"); + ResultSet tableRs = getMetaData().getColumns(catalog, schema, table, column); + + return new ResultSetIterator(tableRs) { + + Map element = new HashMap(); + protected Map convertRow(ResultSet rs) throws SQLException { + element.clear(); + putTablePart(element, rs); + element.put("DATA_TYPE", Integer.valueOf(rs.getInt("DATA_TYPE"))); + element.put("TYPE_NAME", rs.getString("TYPE_NAME")); + element.put("COLUMN_NAME", rs.getString("COLUMN_NAME")); + element.put("NULLABLE", Integer.valueOf(rs.getInt("NULLABLE"))); + element.put("COLUMN_SIZE", Integer.valueOf(rs.getInt("COLUMN_SIZE"))); + element.put("DECIMAL_DIGITS", Integer.valueOf(rs.getInt("DECIMAL_DIGITS"))); + element.put("REMARKS", rs.getString("REMARKS")); + return element; + } + protected Throwable handleSQLException(SQLException e) { + throw new RuntimeException("Error while reading column meta data for " + TableNameQualifier.qualify(catalog, schema, table), e); + } + }; + } catch (SQLException e) { + throw new RuntimeException("Error while reading column meta data for " + TableNameQualifier.qualify(xcatalog, xschema, xtable), e); + } + } + + public Iterator> getPrimaryKeys(final String xcatalog, final String xschema, final String xtable) { + try { + final String catalog = caseForSearch( xcatalog ); + final String schema = caseForSearch( xschema ); + final String table = caseForSearch( xtable ); + + log.debug("getPrimaryKeys(" + catalog + "." + schema + "." + table + ")"); + ResultSet tableRs = getMetaData().getPrimaryKeys(catalog, schema, table); + + return new ResultSetIterator(tableRs) { + + Map element = new HashMap(); + protected Map convertRow(ResultSet rs) throws SQLException { + element.clear(); + putTablePart(element, rs); + element.put("COLUMN_NAME", rs.getString("COLUMN_NAME")); + element.put("KEY_SEQ", Short.valueOf(rs.getShort("KEY_SEQ"))); + element.put("PK_NAME", rs.getString("PK_NAME")); + return element; + } + protected Throwable handleSQLException(SQLException e) { + throw new RuntimeException( + "Error while reading primary key meta data for " + TableNameQualifier.qualify(catalog, schema, table), + e); + } + }; + } catch (SQLException e) { + throw new RuntimeException( + "Error while reading primary key meta data for " + TableNameQualifier.qualify(xcatalog, xschema, xtable), e); + } + } + + public Iterator> getExportedKeys(final String xcatalog, final String xschema, final String xtable) { + try { + final String catalog = caseForSearch( xcatalog ); + final String schema = caseForSearch( xschema ); + final String table = caseForSearch( xtable ); + + log.debug("getExportedKeys(" + catalog + "." + schema + "." + table + ")"); + ResultSet tableRs = getMetaData().getExportedKeys(catalog, schema, table); + + return new ResultSetIterator(tableRs) { + + Map element = new HashMap(); + protected Map convertRow(ResultSet rs) throws SQLException { + element.clear(); + putExportedKeysPart( element, rs ); + return element; + } + protected Throwable handleSQLException(SQLException e) { + throw new RuntimeException( + "Error while reading exported keys meta data for " + TableNameQualifier.qualify(catalog, schema, table), e); + } + }; + } catch (SQLException e) { + throw new RuntimeException( + "Error while reading exported keys meta data for " + TableNameQualifier.qualify(xcatalog, xschema, xtable), e); + } + } + + protected void putExportedKeysPart(Map element, ResultSet rs) throws SQLException { + element.put( "PKTABLE_NAME", rs.getString("PKTABLE_NAME")); + element.put( "PKTABLE_SCHEM", rs.getString("PKTABLE_SCHEM")); + element.put( "PKTABLE_CAT", rs.getString("PKTABLE_CAT")); + element.put( "FKTABLE_CAT", rs.getString("FKTABLE_CAT")); + element.put( "FKTABLE_SCHEM",rs.getString("FKTABLE_SCHEM")); + element.put( "FKTABLE_NAME", rs.getString("FKTABLE_NAME")); + element.put( "FKCOLUMN_NAME", rs.getString("FKCOLUMN_NAME")); + element.put( "PKCOLUMN_NAME", rs.getString("PKCOLUMN_NAME")); + element.put( "FK_NAME", rs.getString("FK_NAME")); + element.put( "KEY_SEQ", Short.valueOf(rs.getShort("KEY_SEQ"))); + } + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/MySQLMetaDataDialect.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/MySQLMetaDataDialect.java new file mode 100644 index 000000000000..b523b1c1a062 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/MySQLMetaDataDialect.java @@ -0,0 +1,99 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.dialect; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public class MySQLMetaDataDialect extends JDBCMetaDataDialect { + + /** + * Based on info from http://dev.mysql.com/doc/refman/5.0/en/show-table-status.html + * Should work on pre-mysql 5 too since it uses the "old" SHOW TABLE command instead of SELECT from infotable. + */ + public Iterator> getSuggestedPrimaryKeyStrategyName(String catalog, String schema, String table) { + String sql = null; + try { + catalog = caseForSearch( catalog ); + schema = caseForSearch( schema ); + table = caseForSearch( table ); + + log.debug("geSuggestedPrimaryKeyStrategyName(" + catalog + "." + schema + "." + table + ")"); + + sql = "show table status " + (catalog==null?"":" from " + catalog + " ") + (table==null?"":" like '" + table + "' "); + PreparedStatement statement = getConnection().prepareStatement( sql ); + + final String sc = schema; + final String cat = catalog; + return new ResultSetIterator(statement.executeQuery()) { + + Map element = new HashMap(); + protected Map convertRow(ResultSet tableRs) throws SQLException { + element.clear(); + element.put("TABLE_NAME", tableRs.getString("NAME")); + element.put("TABLE_SCHEM", sc); + element.put("TABLE_CAT", cat); + + String string = tableRs.getString("AUTO_INCREMENT"); + if(string==null) { + element.put("HIBERNATE_STRATEGY", null); + } else { + element.put("HIBERNATE_STRATEGY", "identity"); + } + return element; + } + protected Throwable handleSQLException(SQLException e) { + // schemaRs and catalogRs are only used for error reporting if + // we get an exception + throw new RuntimeException( + "Could not get list of suggested identity strategies from database. Probably a JDBC driver problem. ", e); + } + }; + } catch (SQLException e) { + throw new RuntimeException("Could not get list of suggested identity strategies from database. Probably a JDBC driver problem. ", e); + } + } + + @Override + public Iterator> getTables( + String xcatalog, + String xschema, + String xtable) { + // MySql JDBC Driver doesn't like 'null' values for the table search pattern, use '%' instead + return super.getTables(xcatalog, xschema, xtable != null ? xtable : "%"); + } + + public Iterator> getColumns( + String xcatalog, + String xschema, + String xtable, + String xcolumn) { + // MySql JDBC Driver doesn't like 'null' values for the table and column search patterns, use '%' instead + return super.getColumns( + xcatalog, + xschema, + xtable != null ? xtable : "%", + xcolumn != null ? xcolumn : "%"); + } + +} + diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/OracleMetaDataDialect.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/OracleMetaDataDialect.java new file mode 100644 index 000000000000..9d3fc5ec13ee --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/OracleMetaDataDialect.java @@ -0,0 +1,743 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.dialect; + +import org.hibernate.tool.reveng.internal.util.TableNameQualifier; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Oracle Specialised MetaData dialect that uses standard JDBC and querys on the + * Data Dictionary for reading metadata. + * + * @author David Channon + * @author Eric Kershner (added preparedstatements HBX-817) + * @author Jacques Stadler (added HBX-1027) + * + */ + +public class OracleMetaDataDialect extends AbstractMetaDataDialect { + + + + public OracleMetaDataDialect() { + super(); + } + + /* ******* TABLE QUERIES ******* */ + private static final String SQL_TABLE_BASE = + "select a.table_name, a.owner, " + + "(SELECT b.comments\n" + + " FROM all_tab_comments b\n" + + " WHERE a.owner = b.owner\n" + + " AND a.table_name = b.table_name) AS comments, " + + "'TABLE' " + + "from all_tables a "; + + private static final String SQL_TABLE_VIEW = + " union all select view_name, owner, NULL, 'VIEW' from all_views "; + + private static final String SQL_TABLE_NONE = SQL_TABLE_BASE + SQL_TABLE_VIEW; + + private static final String SQL_TABLE_SCHEMA = SQL_TABLE_BASE + + "where a.owner like ? " + SQL_TABLE_VIEW + " where owner like ?"; + + private static final String SQL_TABLE_TABLE = SQL_TABLE_BASE + + "where a.table_name like ?" + SQL_TABLE_VIEW + "where view_name like ?"; + + private static final String SQL_TABLE_SCHEMA_AND_TABLE = + SQL_TABLE_BASE + + "where a.owner like ? and a.table_name like ?" + + SQL_TABLE_VIEW + + "where owner like ? and view_name like ?"; + + private PreparedStatement prepTableNone; + + private PreparedStatement prepTableSchema; + + private PreparedStatement prepTableTable; + + private PreparedStatement prepTableSchemaAndTable; + + /* ***************************** */ + /* ******* INDEX QUERIES ******* */ + /* ***************************** */ + private static final String SQL_INDEX_BASE = + "SELECT a.column_name\n" + + " ,decode((SELECT b.uniqueness\n" + + " FROM all_indexes b\n" + + " WHERE a.table_name = b.table_name\n" + + " AND a.table_owner = b.table_owner\n" + + " AND a.index_name = b.index_name\n" + + " AND b.index_type NOT LIKE 'FUNCTION-BASED%'), 'UNIQUE', 'false', 'true') AS uniqueness\n" + + " ,a.index_owner\n" + + " ,a.index_name\n" + + " ,a.table_name\n" + + " FROM all_ind_columns a\n " + + " WHERE 1 = 1\n "; + + private static final String SQL_INDEX_ORDER = " order by a.table_name, a.column_position"; + + private static final String SQL_INDEX_NONE = SQL_INDEX_BASE + + SQL_INDEX_ORDER; + + private static final String SQL_INDEX_SCHEMA = SQL_INDEX_BASE + + "and a.table_owner like ? " + SQL_INDEX_ORDER; + + private static final String SQL_INDEX_TABLE = SQL_INDEX_BASE + + "and a.table_name like ? " + SQL_INDEX_ORDER; + + private static final String SQL_INDEX_SCHEMA_AND_TABLE = SQL_INDEX_BASE + + "and a.table_owner like ? and a.table_name like ? " + SQL_INDEX_ORDER; + + private PreparedStatement prepIndexNone; + + private PreparedStatement prepIndexSchema; + + private PreparedStatement prepIndexTable; + + private PreparedStatement prepIndexSchemaAndTable; + + /* ****** COLUMN QUERIES ******* */ + private static final String SQL_COLUMN_BASE = + "SELECT a.column_name AS COLUMN_NAME\n" + + " ,a.owner AS TABLE_SCHEM\n" + + " ,decode(a.nullable, 'N', 0, 1) AS NULLABLE\n" + + " ,decode(a.data_type, 'FLOAT', decode(a.data_precision, NULL, a.data_length, a.data_precision), 'NUMBER',\n" + + " decode(a.data_precision, NULL, a.data_length, a.data_precision), 'VARCHAR2', a.char_length, 'VARCHAR',\n" + + " a.char_length, 'NVARCHAR2', a.char_length, 'CHAR', a.char_length, 'NCHAR', a.char_length, a.data_length) AS COLUMN_SIZE\n" + + " ,CASE\n" + + " WHEN a.data_type LIKE 'TIMESTAMP%' THEN\n" + + " 93\n" + + " ELSE\n" + + " decode(a.data_type, 'CHAR', 1, 'DATE', 91, 'FLOAT', 6, 'LONG', -1, 'NUMBER', 2, 'VARCHAR2', 12, 'BFILE', -13,\n" + + " 'BLOB', 2004, 'CLOB', 2005, 'MLSLABEL', 1111, 'NCHAR', 1, 'NCLOB', 2005, 'NVARCHAR2', 12, 'RAW', -3,\n" + + " 'ROWID', 1111, 'UROWID', 1111, 'LONG RAW', -4, 'XMLTYPE', 2005, 1111)\n" + + " END AS DATA_TYPE\n" + + " ,a.table_name AS TABLE_NAME\n" + + " ,a.data_type AS TYPE_NAME\n" + + " ,decode(a.data_scale, NULL, 0, a.data_scale) AS DECIMAL_DIGITS\n" + + " ,(SELECT b.comments\n" + + " FROM all_col_comments b\n" + + " WHERE a.owner = b.owner\n" + + " AND a.table_name = b.table_name\n" + + " AND a.column_name = b.column_name) AS COMMENTS\n" + + " FROM all_tab_columns a\n"; + + private static final String SQL_COLUMN_ORDER = " order by column_id "; + + private static final String SQL_COLUMN_NONE = SQL_COLUMN_BASE + + SQL_COLUMN_ORDER; + + private static final String SQL_COLUMN_SCHEMA = SQL_COLUMN_BASE + + "where a.owner like ? " + SQL_COLUMN_ORDER; + + private static final String SQL_COLUMN_TABLE = SQL_COLUMN_BASE + + "where a.table_name like ? " + SQL_COLUMN_ORDER; + + private static final String SQL_COLUMN_COLUMN = SQL_COLUMN_BASE + + "where a.column_name like ? " + SQL_COLUMN_ORDER; + + private static final String SQL_COLUMN_SCHEMA_AND_TABLE = SQL_COLUMN_BASE + + "where a.owner like ? and a.table_name like ? " + SQL_COLUMN_ORDER; + + private static final String SQL_COLUMN_SCHEMA_AND_COLUMN = SQL_COLUMN_BASE + + "where a.owner like ? and a.column_name like ? " + SQL_COLUMN_ORDER; + + private static final String SQL_COLUMN_TABLE_AND_COLUMN = SQL_COLUMN_BASE + + "where a.table_name like ? and a.column_name like ? " + + SQL_COLUMN_ORDER; + + private static final String SQL_COLUMN_SCHEMA_AND_TABLE_AND_COLUMN = SQL_COLUMN_BASE + + "where a.owner like ? and a.table_name like ? and a.column_name like ? " + + SQL_COLUMN_ORDER; + + private PreparedStatement prepColumnNone; + + private PreparedStatement prepColumnSchema; + + private PreparedStatement prepColumnTable; + + private PreparedStatement prepColumnColumn; + + private PreparedStatement prepColumnSchemaAndTable; + + private PreparedStatement prepColumnSchemaAndColumn; + + private PreparedStatement prepColumnTableAndColumn; + + private PreparedStatement prepColumnSchemaAndTableAndColumn; + + /* ***************************** */ + /* ******** PK QUERIES ********* */ + /* ***************************** */ + private static final String SQL_PK_BASE = + "select c.table_name, c.column_name, c.position, c.constraint_name, " + + "c.owner from all_cons_columns c join all_constraints k on " + + "(k.owner = c.owner AND k.table_name = c.table_name AND k.constraint_name = c.constraint_name) " + + "where k.constraint_type = 'P' "; + + private static final String SQL_PK_ORDER = " order by c.table_name, c.constraint_name, c.position desc "; + + private static final String SQL_PK_NONE = SQL_PK_BASE + SQL_PK_ORDER; + + private static final String SQL_PK_SCHEMA = SQL_PK_BASE + + " and c.owner like ? escape '\\' " + SQL_PK_ORDER; + + private static final String SQL_PK_TABLE = SQL_PK_BASE + + " and c.table_name like ? escape '\\' " + SQL_PK_ORDER; + + private static final String SQL_PK_SCHEMA_AND_TABLE = SQL_PK_BASE + + " and c.owner like ? escape '\\' and c.table_name like ? escape '\\' " + SQL_PK_ORDER; + + private PreparedStatement prepPkNone; + + private PreparedStatement prepPkSchema; + + private PreparedStatement prepPkTable; + + private PreparedStatement prepPkSchemaAndTable; + + /* ***************************** */ + /* ******** FK QUERIES ********* */ + /* ***************************** */ + private static final String SQL_FK_BASE = + "SELECT p.table_name as p_table_name\n" + + " ,p.owner as p_owner\n" + + " ,f.owner as f_owner\n" + + " ,f.table_name as f_table_name\n" + + " ,(SELECT fc.column_name\n" + + " FROM all_cons_columns fc\n" + + " WHERE fc.owner = f.owner\n" + + " AND fc.constraint_name = f.constraint_name\n" + + " AND fc.table_name = f.table_name\n" + + " AND fc.position = pc.position) AS fc_column_name\n" + + " ,pc.column_name as pc_column_name\n" + + " ,f.constraint_name\n" + + " ,(SELECT fc.position\n" + + " FROM all_cons_columns fc\n" + + " WHERE fc.owner = f.owner\n" + + " AND fc.constraint_name = f.constraint_name\n" + + " AND fc.table_name = f.table_name\n" + + " AND fc.position = pc.position) AS fc_position\n" + + " FROM all_constraints p\n" + + " JOIN all_cons_columns pc\n" + + " ON pc.owner = p.owner\n" + + " AND pc.constraint_name = p.constraint_name\n" + + " AND pc.table_name = p.table_name\n" + + " JOIN all_constraints f\n" + + " ON p.owner = f.r_owner\n" + + " AND p.constraint_name = f.r_constraint_name\n" + + " WHERE f.constraint_type = 'R'\n" + + " AND p.constraint_type = 'P'\n"; + + private static final String SQL_FK_ORDER = " order by f.table_name, f.constraint_name, position "; + + private static final String SQL_FK_NONE = SQL_FK_BASE + SQL_FK_ORDER; + + private static final String SQL_FK_SCHEMA = SQL_FK_BASE + + " and p.owner like ? " + SQL_FK_ORDER; + + private static final String SQL_FK_TABLE = SQL_FK_BASE + + " and p.table_name like ? " + SQL_FK_ORDER; + + private static final String SQL_FK_SCHEMA_AND_TABLE = SQL_FK_BASE + + " and p.owner like ? and p.table_name like ? " + SQL_FK_ORDER; + + private PreparedStatement prepFkNone; + + private PreparedStatement prepFkSchema; + + private PreparedStatement prepFkTable; + + private PreparedStatement prepFkSchemaAndTable; + + public Iterator> getTables(final String catalog, final String schema, + String table) { + try { + log.debug("getTables(" + catalog + "." + schema + "." + table + ")"); + + ResultSet tableRs = getTableResultSet( schema, table ); + + return new ResultSetIterator(null, tableRs) { + + Map element = new HashMap(); + + + protected Map convertRow(ResultSet tableResultSet) + throws SQLException { + element.clear(); + element.put("TABLE_NAME", tableResultSet.getString(1)); + element.put("TABLE_SCHEM", tableResultSet.getString(2)); + element.put("TABLE_CAT", null); + element.put("TABLE_TYPE", tableResultSet.getString(4)); + element.put("REMARKS", tableResultSet.getString(3)); + log.info( element.toString() ); + return element; + } + + protected Throwable handleSQLException(SQLException e) { + // schemaRs and catalogRs are only used for error reporting + // if + // we get an exception + String databaseStructure = getDatabaseStructure(catalog, + schema); + throw new RuntimeException( + "Could not get list of tables from database. Probably a JDBC driver problem. " + + databaseStructure, + e); + } + }; + } catch (SQLException e) { + // schemaRs and catalogRs are only used for error reporting if we + // get an exception + String databaseStructure = getDatabaseStructure(catalog, schema); + throw new RuntimeException( + "Could not get list of tables from database. Probably a JDBC driver problem. " + + databaseStructure, + e); + } + } + + public Iterator> getIndexInfo(final String catalog, final String schema, + final String table) { + try { + log.debug("getIndexInfo(" + catalog + "." + schema + "." + table + ")"); + + ResultSet indexRs; + indexRs = getIndexInfoResultSet( schema, table ); + + return new ResultSetIterator(null, indexRs) { + + Map element = new HashMap(); + + + protected Map convertRow(ResultSet rs) throws SQLException { + element.clear(); + element.put("COLUMN_NAME", rs.getString(1)); + element.put("TYPE", Short.valueOf((short) 1)); // CLUSTERED + // INDEX + element.put("NON_UNIQUE", Boolean.valueOf(rs.getString(2))); + element.put("TABLE_SCHEM", rs.getString(3)); + element.put("INDEX_NAME", rs.getString(4)); + element.put("TABLE_CAT", null); + element.put("TABLE_NAME", rs.getString(5)); + + return element; + } + + protected Throwable handleSQLException(SQLException e) { + throw new RuntimeException( + "Exception while getting index info for " + + TableNameQualifier.qualify(catalog, schema, table), + e); + } + }; + } catch (SQLException e) { + throw new RuntimeException ( + "Exception while getting index info for " + + TableNameQualifier.qualify(catalog, schema, table) + ": " + e.getMessage(), + e); + } + } + + public Iterator> getColumns(final String catalog, final String schema, + final String table, String column) { + + try { + log.debug("getColumns(" + catalog + "." + schema + "." + table + "." + column + ")"); + + ResultSet columnRs; + columnRs = getColumnsResultSet( schema, table, column ); + + return new ResultSetIterator(null, columnRs) { + + Map element = new HashMap(); + + + protected Map convertRow(ResultSet rs) throws SQLException { + element.clear(); + element.put("COLUMN_NAME", rs.getString(1)); + element.put("TABLE_SCHEM", rs.getString(2)); + element.put("NULLABLE", Integer.valueOf(rs.getInt(3))); + element.put("COLUMN_SIZE", Integer.valueOf(rs.getInt(4))); + element.put("DATA_TYPE", Integer.valueOf(rs.getInt(5))); + element.put("TABLE_NAME", rs.getString(6)); + element.put("TYPE_NAME", rs.getString(7)); + element.put("DECIMAL_DIGITS", Integer.valueOf(rs.getInt(8))); + element.put("TABLE_CAT", null); + element.put("REMARKS", rs.getString(9)); + return element; + } + + protected Throwable handleSQLException(SQLException e) { + throw new RuntimeException( + "Error while reading column meta data for " + + TableNameQualifier.qualify(catalog, schema, table), + e); + } + }; + } catch (SQLException e) { + throw new RuntimeException( + "Error while reading column meta data for " + + TableNameQualifier.qualify(catalog, schema, table), + e); + } + } + + public Iterator> getPrimaryKeys(final String catalog, final String schema, + final String table) { + + try { + log.debug("getPrimaryKeys(" + catalog + "." + schema + "." + table + + ")"); + + ResultSet pkeyRs; + pkeyRs = getPrimaryKeysResultSet( schema, table ); + + return new ResultSetIterator(null, pkeyRs) { + + Map element = new HashMap(); + + + protected Map convertRow(ResultSet rs) throws SQLException { + element.clear(); + element.put("TABLE_NAME", rs.getString(1)); + element.put("COLUMN_NAME", rs.getString(2)); + element.put("KEY_SEQ", Short.valueOf(rs.getShort(3))); + element.put("PK_NAME", rs.getString(4)); + element.put("TABLE_SCHEM", rs.getString(5)); + element.put("TABLE_CAT", null); + return element; + } + + protected Throwable handleSQLException(SQLException e) { + throw new RuntimeException( + "Error while reading primary key meta data for " + + TableNameQualifier.qualify(catalog, schema, table), + e); + } + }; + } catch (SQLException e) { + throw new RuntimeException( + "Error while reading primary key meta data for " + + TableNameQualifier.qualify(catalog, schema, table), + e); + } + } + + public Iterator> getExportedKeys(final String catalog, final String schema, + final String table) { + + try { + log.debug("getExportedKeys(" + catalog + "." + schema + "." + table + + ")"); + + ResultSet pExportRs = getExportedKeysResultSet( schema, table ); + + return new ResultSetIterator(null, pExportRs) { + + Map element = new HashMap(); + + + protected Map convertRow(ResultSet rs) throws SQLException { + element.clear(); + element.put("PKTABLE_NAME", rs.getString(1)); + element.put("PKTABLE_SCHEM", rs.getString(2)); + element.put("PKTABLE_CAT", null); + element.put("FKTABLE_CAT", null); + element.put("FKTABLE_SCHEM", rs.getString(3)); + element.put("FKTABLE_NAME", rs.getString(4)); + element.put("FKCOLUMN_NAME", rs.getString(5)); + element.put("PKCOLUMN_NAME", rs.getString(6)); + element.put("FK_NAME", rs.getString(7)); + element.put("KEY_SEQ", Short.valueOf(rs.getShort(8))); + return element; + } + + protected Throwable handleSQLException(SQLException e) { + throw new RuntimeException( + "Error while reading exported keys meta data for " + + TableNameQualifier.qualify(catalog, schema, table), + e); + } + }; + } catch (SQLException e) { + throw new RuntimeException( + "Error while reading exported keys meta data for " + + TableNameQualifier.qualify(catalog, schema, table), + e); + } + } + + public void close() { + try { + prepTableNone = close( prepTableNone ); + prepTableSchema = close( prepTableSchema ); + prepTableTable = close( prepTableTable ); + prepTableSchemaAndTable = close( prepTableSchemaAndTable ); + prepIndexNone = close( prepIndexNone ); + prepIndexSchema = close( prepIndexSchema ); + prepIndexTable = close( prepIndexTable ); + prepIndexSchemaAndTable = close( prepIndexSchemaAndTable ); + prepColumnNone = close( prepColumnNone ); + prepColumnSchema = close( prepColumnSchema ); + prepColumnTable = close( prepColumnTable ); + prepColumnColumn = close( prepColumnColumn ); + prepColumnSchemaAndTable = close( prepColumnSchemaAndTable ); + prepColumnSchemaAndColumn = close( prepColumnSchemaAndColumn ); + prepColumnTableAndColumn = close( prepColumnTableAndColumn ); + prepColumnSchemaAndTableAndColumn = close( prepColumnSchemaAndTableAndColumn ); + prepPkNone = close( prepPkNone ); + prepPkSchema = close( prepPkSchema ); + prepPkTable = close( prepPkTable ); + prepPkSchemaAndTable = close( prepPkSchemaAndTable ); + prepFkNone = close( prepFkNone ); + prepFkSchema = close( prepFkSchema ); + prepFkTable = close( prepFkTable ); + prepFkSchemaAndTable = close( prepFkSchemaAndTable ); + } + finally { + super.close(); + } + } + + private PreparedStatement close(PreparedStatement ps) { + if(ps==null) { + return null; + } else { + try { + ps.close(); + } + catch (SQLException e) { + throw new RuntimeException( + "Problem while closing prepared statement", e); + } + return null; + } + + } + + private String escape(String str) { + return str.replace("_", "\\_"); + } + + private ResultSet getPrimaryKeysResultSet(final String schem, final String tab) throws SQLException { + String schema = escape(schem); + String table = escape(tab); + if(prepPkNone==null) { + // Prepare primary key queries + log.debug("Preparing primary key queries..."); + Connection con = getConnection(); + prepPkNone = con .prepareStatement(SQL_PK_NONE); + prepPkSchema = con.prepareStatement(SQL_PK_SCHEMA); + prepPkTable = con.prepareStatement(SQL_PK_TABLE); + prepPkSchemaAndTable = con + .prepareStatement(SQL_PK_SCHEMA_AND_TABLE); + log.debug(" primary key queries prepared!"); + } + + ResultSet pkeyRs; + if (schema == null && table == null) { + pkeyRs = prepPkNone.executeQuery(); + } else if (schema != null) { + if (table == null) { + prepPkSchema.setString(1, schema); + pkeyRs = prepPkSchema.executeQuery(); + } else { + prepPkSchemaAndTable.setString(1, schema); + prepPkSchemaAndTable.setString(2, table); + pkeyRs = prepPkSchemaAndTable.executeQuery(); + } + } else { + prepPkTable.setString(1, table); + pkeyRs = prepPkTable.executeQuery(); + } + return pkeyRs; + } + + private ResultSet getIndexInfoResultSet(final String schema, final String table) throws SQLException { + if(prepIndexNone==null) { + // Prepare index queries + log.debug("Preparing index queries..."); + Connection con = getConnection(); + prepIndexNone = con.prepareStatement(SQL_INDEX_NONE); + prepIndexSchema = con.prepareStatement(SQL_INDEX_SCHEMA); + prepIndexTable = con.prepareStatement(SQL_INDEX_TABLE); + prepIndexSchemaAndTable = con.prepareStatement(SQL_INDEX_SCHEMA_AND_TABLE); + log.debug(" ...index queries prepared!"); + } + ResultSet indexRs; + if (schema == null && table == null) { + indexRs = prepIndexNone.executeQuery(); + } else if (schema != null) { + if (table == null) { + prepIndexSchema.setString(1, schema); + indexRs = prepIndexSchema.executeQuery(); + } else { + prepIndexSchemaAndTable.setString(1, schema); + prepIndexSchemaAndTable.setString(2, table); + indexRs = prepIndexSchemaAndTable.executeQuery(); + } + } else { + prepIndexTable.setString(1, table); + indexRs = prepIndexTable.executeQuery(); + } + return indexRs; + } + + private ResultSet getExportedKeysResultSet(final String schema, final String table) throws SQLException { + if(prepFkNone==null) { + // Prepare foreign key queries + log.debug("Preparing foreign key queries..."); + Connection con = getConnection(); + prepFkNone = con .prepareStatement(SQL_FK_NONE); + prepFkSchema = con.prepareStatement(SQL_FK_SCHEMA); + prepFkTable = con.prepareStatement(SQL_FK_TABLE); + prepFkSchemaAndTable = con.prepareStatement(SQL_FK_SCHEMA_AND_TABLE); + log.debug(" foreign key queries prepared!"); + } + + ResultSet pExportRs; + if (schema == null && table == null) { + pExportRs = prepFkNone.executeQuery(); + } else if (schema != null) { + if (table == null) { + prepFkSchema.setString(1, schema); + pExportRs = prepFkSchema.executeQuery(); + } else { + prepFkSchemaAndTable.setString(1, schema); + prepFkSchemaAndTable.setString(2, table); + pExportRs = prepFkSchemaAndTable.executeQuery(); + } + } else { + prepFkTable.setString(1, table); + pExportRs = prepFkTable.executeQuery(); + } + return pExportRs; + } + private ResultSet getColumnsResultSet(final String schema, final String table, String column) throws SQLException { + + if(prepColumnNone==null) { + // Prepare column queries + log.debug("Preparing column queries..."); + Connection con = getConnection(); + prepColumnNone = con.prepareStatement(SQL_COLUMN_NONE); + prepColumnSchema = con.prepareStatement(SQL_COLUMN_SCHEMA); + prepColumnTable = con.prepareStatement(SQL_COLUMN_TABLE); + prepColumnColumn = con.prepareStatement(SQL_COLUMN_COLUMN); + prepColumnSchemaAndTable = con.prepareStatement(SQL_COLUMN_SCHEMA_AND_TABLE); + prepColumnSchemaAndColumn = con.prepareStatement(SQL_COLUMN_SCHEMA_AND_COLUMN); + prepColumnTableAndColumn = con.prepareStatement(SQL_COLUMN_TABLE_AND_COLUMN); + prepColumnSchemaAndTableAndColumn = con.prepareStatement(SQL_COLUMN_SCHEMA_AND_TABLE_AND_COLUMN); + log.debug(" ...column queries prepared!"); + } + + ResultSet columnRs; + // No parameters specified + if (schema == null && table == null && column == null) { + columnRs = prepColumnNone.executeQuery(); + } else if (schema != null) { + if (table == null) { + if (column == null) { + // Schema specified + prepColumnSchema.setString(1, schema); + columnRs = prepColumnSchema.executeQuery(); + } else { + // Schema and column specified + prepColumnSchemaAndColumn.setString(1, schema); + prepColumnSchemaAndColumn.setString(2, column); + columnRs = prepColumnSchemaAndColumn.executeQuery(); + } + } else { + if (column == null) { + // Schema and table specified + prepColumnSchemaAndTable.setString(1, schema); + prepColumnSchemaAndTable.setString(2, table); + columnRs = prepColumnSchemaAndTable.executeQuery(); + } else { + // Schema, table and column specified + prepColumnSchemaAndTableAndColumn.setString(1, schema); + prepColumnSchemaAndTableAndColumn.setString(2, table); + prepColumnSchemaAndTableAndColumn.setString(3, column); + columnRs = prepColumnSchemaAndTableAndColumn.executeQuery(); + } + } + } else { + if (table == null) { + // Column specified + prepColumnColumn.setString(1, column); + columnRs = prepColumnColumn.executeQuery(); + } else { + if (column == null) { + // Table specified + prepColumnTable.setString(1, table); + columnRs = prepColumnTable.executeQuery(); + } else { + // Table and column specified + prepColumnTableAndColumn.setString(1, table); + prepColumnTableAndColumn.setString(2, column); + columnRs = prepColumnTableAndColumn.executeQuery(); + + } + } + } + return columnRs; + } + + private ResultSet getTableResultSet(final String schema, String table) throws SQLException { + ResultSet tableRs; + if(prepTableNone==null) { + // Prepare table queries + log.debug("Preparing table queries..."); + Connection connection2 = getConnection(); + prepTableNone = connection2.prepareStatement(SQL_TABLE_NONE); + prepTableSchema = connection2.prepareStatement(SQL_TABLE_SCHEMA); + prepTableTable = connection2.prepareStatement(SQL_TABLE_TABLE); + prepTableSchemaAndTable = connection2.prepareStatement(SQL_TABLE_SCHEMA_AND_TABLE); + log.debug(" ...table queries prepared!"); + } + if (schema == null && table == null) { + tableRs = prepTableNone.executeQuery(); + } else if (schema != null) { + if (table == null) { + prepTableSchema.setString(1, schema); + prepTableSchema.setString(2, schema); + tableRs = prepTableSchema.executeQuery(); + } else { + prepTableSchemaAndTable.setString(1, schema); + prepTableSchemaAndTable.setString(2, table); + prepTableSchemaAndTable.setString(3, schema); + prepTableSchemaAndTable.setString(4, table); + tableRs = prepTableSchemaAndTable.executeQuery(); + } + } else { + prepTableTable.setString(1, table); + prepTableTable.setString(2, table); + tableRs = prepTableTable.executeQuery(); + } + return tableRs; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/ResultSetIterator.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/ResultSetIterator.java new file mode 100644 index 000000000000..712697d30987 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/ResultSetIterator.java @@ -0,0 +1,111 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.dialect; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; + + +/** + * Iterator over a resultset; intended usage only for metadata reading. + */ +public abstract class ResultSetIterator implements Iterator> { + + private ResultSet rs; + + protected boolean current = false; + + protected boolean endOfRows = false; + + private Statement statement = null; + + protected ResultSetIterator(ResultSet resultset) { + this(null, resultset); + } + + public ResultSetIterator(Statement stmt, ResultSet resultset) { + this.rs = resultset; + this.statement = stmt; + } + + public boolean hasNext() { + try { + advance(); + return !endOfRows; + } + catch (SQLException e) { + handleSQLException( e ); + return false; + } + } + + + public Map next() { + try { + advance(); + if ( endOfRows ) { + throw new NoSuchElementException(); + } + current = false; + return convertRow( rs ); + } + catch (SQLException e) { + handleSQLException(e); + throw new NoSuchElementException("excpetion occurred " + e); + } + + } + + abstract protected Throwable handleSQLException(SQLException e); + abstract protected Map convertRow(ResultSet rs) throws SQLException; + + public void remove() { + throw new UnsupportedOperationException( + "remove() not possible on ResultSet" ); + } + + protected void advance() throws SQLException { + + if ( !current && !endOfRows ) { + if ( rs.next() ) { + current = true; + endOfRows = false; + } + else { + current = false; + endOfRows = true; + } + } + } + + public void close() { + try { + rs.close(); + if(statement!=null) { + statement.close(); + } + } + catch (SQLException e) { + handleSQLException(e); + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/SQLServerMetaDataDialect.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/SQLServerMetaDataDialect.java new file mode 100644 index 000000000000..6a209b95c913 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/dialect/SQLServerMetaDataDialect.java @@ -0,0 +1,95 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.dialect; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * + * @author ddukker + * + */ +public class SQLServerMetaDataDialect extends JDBCMetaDataDialect { + + public Iterator> getSuggestedPrimaryKeyStrategyName(String catalog, String schema, String table) { + String sql = null; + try { + catalog = caseForSearch( catalog ); + schema = caseForSearch( schema ); + table = caseForSearch( table ); + + log.debug("geSuggestedPrimaryKeyStrategyName(" + catalog + "." + schema + "." + table + ")"); + + sql = "SELECT a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME as table_name, c.DATA_TYPE as data_type, b.CONSTRAINT_TYPE, OBJECTPROPERTY(OBJECT_ID(a.TABLE_NAME),'TableHasIdentity') as hasIdentity " + + "FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE a " + + "INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS b on a.CONSTRAINT_NAME = b.CONSTRAINT_NAME " + + "INNER JOIN INFORMATION_SCHEMA.COLUMNS c on a.TABLE_CATALOG = c.TABLE_CATALOG AND a.TABLE_SCHEMA = c.TABLE_SCHEMA AND a.TABLE_NAME = c.TABLE_NAME AND a.COLUMN_NAME = c.COLUMN_NAME " + + "WHERE a.TABLE_NAME=? AND a.TABLE_SCHEMA=? AND a.TABLE_CATALOG=? AND b.CONSTRAINT_TYPE = 'Primary key'"; + + PreparedStatement statement = getConnection().prepareStatement( sql ); + statement.setString(1, table); + statement.setString( 2, schema ); + statement.setString( 3, catalog ); + + final String sc = schema; + final String cat = catalog; + return new ResultSetIterator(statement.executeQuery()) { + + final Map element = new HashMap(); + protected Map convertRow(ResultSet tableRs) throws SQLException { + element.clear(); + element.put("TABLE_NAME", tableRs.getString("table_name")); + element.put("TABLE_SCHEM", sc); + element.put("TABLE_CAT", cat); + + String string = tableRs.getString("data_type"); + + boolean bool = tableRs.getBoolean("hasIdentity"); + if(string!=null) { + if(string.equalsIgnoreCase("uniqueidentifier")){ + element.put("HIBERNATE_STRATEGY", "guid"); + }else if(bool){ + element.put("HIBERNATE_STRATEGY", "identity"); + }else{ + element.put("HIBERNATE_STRATEGY", null); + } + }else { + element.put("HIBERNATE_STRATEGY", null); + } + return element; + } + protected Throwable handleSQLException(SQLException e) { + // schemaRs and catalogRs are only used for error reporting if + // we get an exception + throw new RuntimeException( + "Could not get list of suggested identity strategies from database. Probably a JDBC driver problem. ", e); + } + }; + } catch (SQLException e) { + throw new RuntimeException( + "Could not get list of suggested identity strategies from database. Probably a JDBC driver problem. ", e); + } + } + + } + diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/BasicColumnProcessor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/BasicColumnProcessor.java new file mode 100644 index 000000000000..18bf24cbf8eb --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/BasicColumnProcessor.java @@ -0,0 +1,161 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.reader; + +import org.hibernate.JDBCException; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.RevengDialect; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.core.util.RevengUtils; +import org.hibernate.tool.reveng.internal.util.JdbcToHibernateTypeHelper; +import org.hibernate.tool.reveng.internal.util.TableNameQualifier; +import org.jboss.logging.Logger; + +import java.sql.DatabaseMetaData; +import java.util.Iterator; +import java.util.Map; + +public class BasicColumnProcessor { + + private static final Logger log = Logger.getLogger(BasicColumnProcessor.class); + + public static void processBasicColumns( + RevengDialect metaDataDialect, + RevengStrategy revengStrategy, + String defaultSchema, String defaultCatalog, + Table table) { + + String qualify = TableNameQualifier.qualify(table.getCatalog(), table.getSchema(), table.getName() ); + Iterator columnIterator = null; + + try { + Map columnRs = null; + log.debug("Finding columns for " + qualify ); + columnIterator = metaDataDialect.getColumns(getCatalogForDBLookup(table.getCatalog(), defaultCatalog), getSchemaForDBLookup(table.getSchema(), defaultSchema), table.getName(), null); + //dumpHeader(columnRs); + while (columnIterator.hasNext() ) { + //dumpRow(columnRs); + columnRs = (Map) columnIterator.next(); + String tableName = (String) columnRs.get("TABLE_NAME"); + int sqlType = ((Integer)columnRs.get("DATA_TYPE")).intValue(); + //String sqlTypeName = (String) columnRs.get("TYPE_NAME"); + String columnName = (String) columnRs.get("COLUMN_NAME"); + String comment = (String) columnRs.get("REMARKS"); + + TableIdentifier ti = RevengUtils.createTableIdentifier(table, defaultCatalog, defaultSchema); + if(revengStrategy.excludeColumn(ti, columnName)) { + log.debug("Column " + ti + "." + columnName + " excluded by strategy"); + continue; + } + if(!tableName.equals(table.getName())) { + log.debug("Table name " + tableName + " does not match requested " + table.getName() + ". Ignoring column " + columnName + " since it either is invalid or a duplicate" ); + continue; + } + + //String columnDefaultValue = columnRs.getString("COLUMN_DEF"); TODO: only read if have a way to avoid issues with clobs/lobs and similar + int dbNullability = ((Integer)columnRs.get("NULLABLE")).intValue(); + boolean isNullable = true; + switch (dbNullability) { + case DatabaseMetaData.columnNullable: + case DatabaseMetaData.columnNullableUnknown: + isNullable = true; + break; + case DatabaseMetaData.columnNoNulls: + isNullable = false; + break; + default: + isNullable = true; + } + + int size = ((Integer)columnRs.get("COLUMN_SIZE")).intValue(); + int decimalDigits = ((Integer)columnRs.get("DECIMAL_DIGITS")).intValue(); + + Column column = new Column(); + column.setName(quote(columnName, metaDataDialect)); + Column existing = table.getColumn(column); + if(existing!=null) { + throw new RuntimeException(column + " already exists in " + qualify); + } + + //TODO: column.setSqlType(sqlTypeName); //this does not work 'cos the precision/scale/length are not retured in TYPE_NAME + //column.setSqlType(sqlTypeName); + column.setComment(comment); + column.setSqlTypeCode(Integer.valueOf(sqlType) ); + if(intBounds(size) ) { + if(JdbcToHibernateTypeHelper.typeHasLength(sqlType) ) { + column.setLength(size); + } + if(JdbcToHibernateTypeHelper.typeHasPrecision(sqlType) ) { + column.setPrecision(size); + } + } + if(intBounds(decimalDigits) ) { + if(JdbcToHibernateTypeHelper.typeHasScale(sqlType) ) { + column.setScale(decimalDigits); + } + } + + column.setNullable(isNullable); + + // columnDefaultValue is useless for Hibernate + // isIndexed (available via Indexes) + // unique - detected when getting indexes + // isPk - detected when finding primary keys + + table.addColumn(column); + } + } + finally { + + if(columnIterator!=null) { + try { + metaDataDialect.close(columnIterator); + } catch(JDBCException se) { + log.warn("Exception while closing iterator for column meta data",se); + } + } + } + + } + + private static String getCatalogForDBLookup(String catalog, String defaultCatalog) { + return catalog==null?defaultCatalog:catalog; + } + + private static String getSchemaForDBLookup(String schema, String defaultSchema) { + return schema==null?defaultSchema:schema; + } + + private static boolean intBounds(int size) { + return size>=0 && size!=Integer.MAX_VALUE; + } + + private static String quote(String columnName, RevengDialect metaDataDialect) { + if(columnName==null) return columnName; + if(metaDataDialect.needQuote(columnName)) { + if(columnName.length()>1 && columnName.charAt(0)=='`' && columnName.charAt(columnName.length()-1)=='`') { + return columnName; // avoid double quoting + } + return "`" + columnName + "`"; + } else { + return columnName; + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/DatabaseReader.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/DatabaseReader.java new file mode 100644 index 000000000000..35ded792081f --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/DatabaseReader.java @@ -0,0 +1,172 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.reader; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.Table; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.tool.reveng.api.core.RevengDialect; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.RevengStrategy.SchemaSelection; +import org.hibernate.tool.reveng.internal.core.RevengMetadataCollector; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +public class DatabaseReader { + + public static DatabaseReader create( + Properties properties, + RevengStrategy revengStrategy, + RevengDialect mdd, + ServiceRegistry serviceRegistry) { + ConnectionProvider connectionProvider = serviceRegistry.getService(ConnectionProvider.class); + return new DatabaseReader(properties, mdd, connectionProvider, revengStrategy); + } + + private final RevengStrategy revengStrategy; + + private RevengDialect metadataDialect; + + private final ConnectionProvider provider; + + private final Properties properties; + + private DatabaseReader( + Properties properties, + RevengDialect dialect, + ConnectionProvider provider, + RevengStrategy reveng) { + this.metadataDialect = dialect; + this.provider = provider; + this.revengStrategy = reveng; + this.properties = properties; + if (revengStrategy == null) { + throw new IllegalStateException("Strategy cannot be null"); + } + } + + public void readDatabaseSchema(RevengMetadataCollector revengMetadataCollector) { + try { + metadataDialect.configure(provider); + TableCollector tableCollector = TableCollector.create( + metadataDialect, + revengStrategy, + revengMetadataCollector, + properties); + for (Iterator iter = getSchemaSelections().iterator(); iter.hasNext();) { + tableCollector.processTables(iter.next()); + } + revengMetadataCollector.setOneToManyCandidates(resolveForeignKeys(revengMetadataCollector)); + + } finally { + metadataDialect.close(); + revengStrategy.close(); + } + } + + /** + * Iterates the tables and find all the foreignkeys that refers to something + * that is available inside the DatabaseCollector. + * + * @param revengMetadataCollector + * @return + */ + private Map> resolveForeignKeys(RevengMetadataCollector revengMetadataCollector) { + List fks = new ArrayList(); + ForeignKeyProcessor foreignKeyProcessor = ForeignKeyProcessor.create( + metadataDialect, + revengStrategy, + getDefaultCatalog(), + getDefaultSchema(), + revengMetadataCollector); + for (Table table : revengMetadataCollector.getTables()) { + // Done here after the basic process of collections as we might not have touched + // all referenced tables (this ensure the columns are the same instances + // througout the basic JDBC derived model. + // after this stage it should be "ok" to divert from keeping columns in sync as + // it can be required if the same + // column is used with different aliases in the ORM mapping. + ForeignKeysInfo foreignKeys = foreignKeyProcessor.processForeignKeys(table); + fks.add(foreignKeys); + } + + Map> oneToManyCandidates = new HashMap>(); + for (Iterator iter = fks.iterator(); iter.hasNext();) { + ForeignKeysInfo element = iter.next(); + Map> map = element.process(revengStrategy); // the actual foreignkey is created + // here. + mergeMultiMap(oneToManyCandidates, map); + } + return oneToManyCandidates; + } + + private void mergeMultiMap(Map> dest, Map> src) { + Iterator>> items = src.entrySet().iterator(); + + while (items.hasNext()) { + Entry> element = items.next(); + + List existing = dest.get(element.getKey()); + if (existing == null) { + dest.put(element.getKey(), element.getValue()); + } else { + existing.addAll(element.getValue()); + } + } + + } + + private List getSchemaSelections() { + List result = revengStrategy.getSchemaSelections(); + if (result == null) { + result = new ArrayList(); + result.add(new SchemaSelection() { + @Override + public String getMatchCatalog() { + return getDefaultCatalog(); + } + @Override + public String getMatchSchema() { + return getDefaultSchema(); + } + @Override + public String getMatchTable() { + return null; + } + }); + } + return result; + } + + private String getDefaultSchema() { + return properties.getProperty(AvailableSettings.DEFAULT_SCHEMA); + } + + private String getDefaultCatalog() { + return properties.getProperty(AvailableSettings.DEFAULT_CATALOG); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/ForeignKeyProcessor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/ForeignKeyProcessor.java new file mode 100644 index 000000000000..c02fd161c2fb --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/ForeignKeyProcessor.java @@ -0,0 +1,369 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.reader; + +import org.hibernate.JDBCException; +import org.hibernate.MappingException; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.RevengDialect; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.core.RevengMetadataCollector; +import org.hibernate.tool.reveng.internal.core.util.RevengUtils; +import org.hibernate.tool.reveng.internal.util.StringUtil; +import org.hibernate.tool.reveng.internal.util.TableNameQualifier; +import org.jboss.logging.Logger; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class ForeignKeyProcessor { + + private static final Logger log = Logger.getLogger(ForeignKeyProcessor.class); + + public static ForeignKeyProcessor create( + RevengDialect metaDataDialect, + RevengStrategy revengStrategy, + String defaultCatalog, + String defaultSchema, + RevengMetadataCollector revengMetadataCollector) { + return new ForeignKeyProcessor( + metaDataDialect, + revengStrategy, + defaultCatalog, + defaultSchema, + revengMetadataCollector); + } + + private final RevengDialect metaDataDialect; + private final RevengStrategy revengStrategy; + private final String defaultSchema; + private final String defaultCatalog; + private final RevengMetadataCollector revengMetadataCollector; + + private short bogusFkName = 0; + + private ForeignKeyProcessor( + RevengDialect metaDataDialect, + RevengStrategy revengStrategy, + String defaultCatalog, + String defaultSchema, + RevengMetadataCollector revengMetadataCollector) { + this.metaDataDialect = metaDataDialect; + this.revengStrategy = revengStrategy; + this.defaultCatalog = defaultCatalog; + this.defaultSchema = defaultSchema; + this.revengMetadataCollector = revengMetadataCollector; + } + + public ForeignKeysInfo processForeignKeys(Table referencedTable) { + // foreign key name to list of columns in dependent table + Map> dependentColumns = new HashMap>(); + // foreign key name to dependent table + Map dependentTables = new HashMap(); + // foreign key name to list of columns in referenced table + Map> referencedColumns = new HashMap>(); + processExportedForeignKeys(referencedTable, dependentColumns, dependentTables, referencedColumns); + processUserForeignKeys(referencedTable, dependentColumns, dependentTables, referencedColumns); + return new ForeignKeysInfo(referencedTable, dependentTables, dependentColumns, referencedColumns); + } + + private void processUserForeignKeys( + Table referencedTable, + Map> dependentColumns, + Map dependentTables, + Map> referencedColumns) { + List userForeignKeys = revengStrategy.getForeignKeys( + RevengUtils.createTableIdentifier( + referencedTable, + defaultCatalog, + defaultSchema)); + if(userForeignKeys!=null) { + Iterator iterator = userForeignKeys.iterator(); + while ( iterator.hasNext() ) { + processUserForeignKey( + iterator.next(), + referencedTable, + referencedColumns, + dependentColumns, + dependentTables); + } + } + } + + private void processExportedForeignKeys( + Table referencedTable, + Map> dependentColumns, + Map dependentTables, + Map> referencedColumns) { + try { + log.debug("Calling getExportedKeys on " + referencedTable); + Iterator> exportedKeyIterator = metaDataDialect.getExportedKeys( + getCatalogForDBLookup(referencedTable.getCatalog(), defaultCatalog), + getSchemaForDBLookup(referencedTable.getSchema(), defaultSchema), + referencedTable.getName() ); + try { + while (exportedKeyIterator.hasNext() ) { + processExportedKey( + exportedKeyIterator.next(), + bogusFkName, + dependentColumns, + dependentTables, + referencedColumns, + referencedTable); + } + } + finally { + try { + if(exportedKeyIterator!=null) { + metaDataDialect.close(exportedKeyIterator); + } + } catch(JDBCException se) { + log.warn("Exception while closing result set for foreign key meta data",se); + } + } + } catch(JDBCException se) { + log.warn("Exception while reading foreign keys for " + referencedTable + " [" + se.toString() + "]", se); + } + } + + private void processExportedKey( + Map exportedKeyRs, + short bogusFkName, + Map> dependentColumns, + Map dependentTables, + Map> referencedColumns, + Table referencedTable) { + String fkName = determineForeignKeyName(exportedKeyRs, bogusFkName); + Table fkTable = determineForeignKeyTable(exportedKeyRs, fkName); + if (fkTable != null) { + log.debug("Foreign key " + fkName); + handleDependencies(exportedKeyRs, dependentColumns, dependentTables, fkTable, fkName); + handleReferences(exportedKeyRs, referencedColumns, referencedTable, fkName); + } + } + + private Table determineForeignKeyTable(Map exportedKeyRs, String fkName) { + Table fkTable = getTable( + (String) exportedKeyRs.get("FKTABLE_CAT"), + (String) exportedKeyRs.get("FKTABLE_SCHEM"), + (String) exportedKeyRs.get("FKTABLE_NAME")); + if (fkTable == null) { + String fkCatalog = getCatalogForModel((String) exportedKeyRs.get("FKTABLE_CAT"), defaultCatalog); + String fkSchema = getSchemaForModel((String) exportedKeyRs.get("FKTABLE_SCHEM"), defaultSchema); + String fkTableName = (String) exportedKeyRs.get("FKTABLE_NAME"); + fkTable = getTable(fkCatalog, fkSchema, fkTableName); + if (fkTable == null) { + log.debug( + "Foreign key " + + fkName + + " references unknown or filtered table " + + TableNameQualifier.qualify(fkCatalog, fkSchema, fkTableName) ); + } + } + return fkTable; + } + + private String determineForeignKeyName( + Map exportedKeyRs, + short bogusFkName) { + String fkName = (String) exportedKeyRs.get("FK_NAME"); + if (fkName == null) { + fkName = Short.toString(bogusFkName++); + } + return fkName; + } + + private void handleReferences( + Map exportedKeyRs, + Map> referencedColumns, + Table referencedTable, + String fkName) { + List primColumns = referencedColumns.get(fkName); + if (primColumns == null) { + primColumns = new ArrayList(); + referencedColumns.put(fkName,primColumns); + } + Column refColumn = new Column((String) exportedKeyRs.get("PKCOLUMN_NAME")); + Column existingColumn = referencedTable.getColumn(refColumn); + if (existingColumn != null) { + primColumns.add(existingColumn); + } else { + primColumns.add(refColumn); + } + } + + private void handleDependencies( + Map exportedKeyRs, + Map> dependentColumns, + Map dependentTables, + Table fkTable, + String fkName) { + String fkColumnName = (String) exportedKeyRs.get("FKCOLUMN_NAME"); + List depColumns = dependentColumns.get(fkName); + if (depColumns == null) { + depColumns = new ArrayList(); + dependentColumns.put(fkName,depColumns); + dependentTables.put(fkName, fkTable); + } + else { + Object previousTable = dependentTables.get(fkName); + if(fkTable != previousTable) { + throw new RuntimeException("Foreign key name (" + fkName + ") mapped to different tables! previous: " + previousTable + " current:" + fkTable); + } + } + Column column = new Column(fkColumnName); + Column existingColumn = fkTable.getColumn(column); + if (existingColumn != null) { + depColumns.add(existingColumn); + } else { + depColumns.add(column); + } + } + + private void processUserForeignKey( + ForeignKey element, + Table referencedTable, + Map> referencedColumns, + Map> dependentColumns, + Map dependentTables) { + + if(!equalTable(referencedTable, element.getReferencedTable(), defaultSchema, defaultCatalog)) { + log.debug("Referenced table " + element.getReferencedTable().getName() + " is not " + referencedTable + ". Ignoring userdefined foreign key " + element ); + return; // skip non related foreign keys + } + Table deptable = determineDependentTable(dependentTables, element); + if(deptable==null) { + // filter out stuff we don't have tables for! + log.debug( + "User defined foreign key " + + element.getName() + + " references unknown or filtered table " + + TableIdentifier.create(element.getTable()) ); + } else { + dependentTables.put(element.getName(), deptable); + referencedColumns.put(element.getName(), getReferencedColums(referencedTable, element) ); + dependentColumns.put(element.getName(), getDependendColumns(deptable, element) ); + } + } + + private Table determineDependentTable(Map dependentTables, ForeignKey element) { + Table userfkTable = element.getTable(); + String userfkName = element.getName(); + Table deptable = dependentTables.get(userfkName); + if(deptable!=null) { // foreign key already defined!? + throw new MappingException("Foreign key " + userfkName + " already defined in the database!"); + } + return getTable( + getCatalogForDBLookup(userfkTable.getCatalog(), defaultCatalog), + getSchemaForDBLookup(userfkTable.getSchema(), defaultSchema), + userfkTable.getName()); + } + + private List getDependendColumns(Table deptable, ForeignKey element) { + List userColumns = element.getColumns(); + List depColumns = new ArrayList(userColumns.size() ); + Iterator colIterator = userColumns.iterator(); + while(colIterator.hasNext() ) { + Column jdbcColumn = (Column) colIterator.next(); + Column column = new Column(jdbcColumn.getName() ); + Column existingColumn = deptable.getColumn(column); + column = existingColumn==null ? column : existingColumn; + depColumns.add(column); + } + return depColumns; + } + + private List getReferencedColums(Table referencedTable, ForeignKey element) { + List userrefColumns = element.getReferencedColumns(); + List result = new ArrayList(userrefColumns.size() ); + Iterator colIterator = userrefColumns.iterator(); + while(colIterator.hasNext() ) { + Column jdbcColumn = (Column) colIterator.next(); + Column column = new Column(jdbcColumn.getName() ); + Column existingColumn = referencedTable.getColumn(column); + column = existingColumn==null ? column : existingColumn; + result.add(column); + } + return result; + + } + + private static String getCatalogForDBLookup(String catalog, String defaultCatalog) { + return catalog==null?defaultCatalog:catalog; + } + + private static String getSchemaForDBLookup(String schema, String defaultSchema) { + return schema==null?defaultSchema:schema; + } + + /** If catalog is equal to defaultCatalog then we return null so it will be null in the generated code. */ + private static String getCatalogForModel(String catalog, String defaultCatalog) { + if(catalog==null) return null; + if(catalog.equals(defaultCatalog)) return null; + return catalog; + } + + /** If catalog is equal to defaultSchema then we return null so it will be null in the generated code. */ + private static String getSchemaForModel(String schema, String defaultSchema) { + if(schema==null) return null; + if(schema.equals(defaultSchema)) return null; + return schema; + } + + private static boolean equalTable( + Table table1, + Table table2, + String defaultSchema, + String defaultCatalog) { + return table1.getName().equals(table2.getName()) + && ( StringUtil.isEqual( + getSchemaForModel(table1.getSchema(), defaultSchema), + getSchemaForModel(table2.getSchema(), defaultSchema)) + && ( StringUtil.isEqual( + getCatalogForModel(table1.getCatalog(), defaultCatalog), + getCatalogForModel(table2.getCatalog(), defaultCatalog)))); + } + + private Table getTable(String catalog, String schema, String name) { + return revengMetadataCollector.getTable( + TableIdentifier.create( + quote(catalog), + quote(schema), + quote(name))); + } + + private String quote(String name) { + if (name == null) + return name; + if (metaDataDialect.needQuote(name)) { + if (name.length() > 1 && name.charAt(0) == '`' + && name.charAt(name.length() - 1) == '`') { + return name; // avoid double quoting + } + return "`" + name + "`"; + } else { + return name; + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/ForeignKeysInfo.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/ForeignKeysInfo.java new file mode 100644 index 000000000000..60cd63640236 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/ForeignKeysInfo.java @@ -0,0 +1,80 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.reader; + +import org.hibernate.mapping.Column; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.TableIdentifier; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public class ForeignKeysInfo { + + final Map dependentTables; + final Map> dependentColumns; + final Map> referencedColumns; + private final Table referencedTable; + + public ForeignKeysInfo( + Table referencedTable, + Map tables, + Map> columns, + Map> refColumns) { + this.referencedTable = referencedTable; + this.dependentTables = tables; + this.dependentColumns = columns; + this.referencedColumns = refColumns; + } + + public Map> process(RevengStrategy revengStrategy) { + Map> oneToManyCandidates = new HashMap>(); + Iterator> iterator = dependentTables.entrySet().iterator(); + while (iterator.hasNext() ) { + Entry entry = iterator.next(); + String fkName = entry.getKey(); + Table fkTable = entry.getValue(); + List columns = dependentColumns.get(fkName); + List refColumns = referencedColumns.get(fkName); + + String className = revengStrategy.tableToClassName(TableIdentifier.create(referencedTable) ); + + ForeignKey key = fkTable.createForeignKey(fkName, columns, className, null, null, refColumns); + key.setReferencedTable(referencedTable); + + addToMultiMap(oneToManyCandidates, className, key); + } + return oneToManyCandidates; + } + + private void addToMultiMap(Map> multimap, String key, ForeignKey item) { + List existing = multimap.get(key); + if(existing == null) { + existing = new ArrayList(); + multimap.put(key, existing); + } + existing.add(item); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/IndexProcessor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/IndexProcessor.java new file mode 100644 index 000000000000..a64752d40823 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/IndexProcessor.java @@ -0,0 +1,184 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.reader; + +import org.hibernate.JDBCException; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Index; +import org.hibernate.mapping.Table; +import org.hibernate.mapping.UniqueKey; +import org.hibernate.tool.reveng.api.core.RevengDialect; +import org.hibernate.tool.reveng.internal.util.TableNameQualifier; +import org.jboss.logging.Logger; + +import java.sql.DatabaseMetaData; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public class IndexProcessor { + + private static final Logger log = Logger.getLogger(IndexProcessor.class); + + public static void processIndices( + RevengDialect metaDataDialect, + String defaultSchema, + String defaultCatalog, + Table table) { + + Map indexes = new HashMap(); // indexname (String) -> Index + Map uniquekeys = new HashMap(); // name (String) -> UniqueKey + Map> uniqueColumns = new HashMap>(); // Column -> List + + Iterator> indexIterator = null; + try { + Map indexRs = null; + indexIterator = metaDataDialect.getIndexInfo(getCatalogForDBLookup(table.getCatalog(), defaultCatalog), getSchemaForDBLookup(table.getSchema(), defaultSchema), table.getName()); + + while (indexIterator.hasNext() ) { + indexRs = indexIterator.next(); + String indexName = (String) indexRs.get("INDEX_NAME"); + String columnName = (String) indexRs.get("COLUMN_NAME"); + boolean unique = !((Boolean)indexRs.get("NON_UNIQUE")).booleanValue(); + + if (columnName != null || indexName != null) { // both can be non-null with statistical indexs which we don't have any use for. + + if(unique) { + UniqueKey key = uniquekeys.get(indexName); + if (key==null) { + key = new UniqueKey(); + key.setName(indexName); + key.setTable(table); + table.addUniqueKey(key); + uniquekeys.put(indexName, key); + } + + if(indexes.containsKey(indexName) ) { + throw new RuntimeException("UniqueKey exists also as Index! "); + } + Column column = getColumn(metaDataDialect, table, columnName); + key.addColumn(column); + + if (unique && key.getColumnSpan()==1) { + // make list of columns that has the chance of being unique + List l = uniqueColumns.get(column); + if (l == null) { + l = new ArrayList(); + uniqueColumns.put(column, l); + } + l.add(key); + } + } + else { + Index index = indexes.get(indexName); + if(index==null) { + index = new Index(); + index.setName(indexName); + index.setTable(table); + table.addIndex(index); + indexes.put(indexName, index); + } + + if(uniquekeys.containsKey(indexName) ) { + throw new RuntimeException("Index exists also as Unique! "); + } + Column column = getColumn(metaDataDialect, table, columnName); + index.addColumn(column); + } + + } + else { + if(DatabaseMetaData.tableIndexStatistic != ((Short)indexRs.get("TYPE")).shortValue() ) { + log.warn("Index was not statistical, but no column name was found in " + indexName); + } + + } + } + } + catch (JDBCException t) { + log.warn("Exception while trying to get indexinfo on " + TableNameQualifier.qualify(table.getCatalog(), table.getSchema(), table.getName() ) + "=" + t.getMessage() ); + // Bug #604761 Oracle getIndexInfo() needs major grants And other dbs sucks too ;) + // http://sourceforge.net/tracker/index.php?func=detail&aid=604761&group_id=36044&atid=415990 + } + finally { + if (indexIterator != null) { + try { + metaDataDialect.close(indexIterator); + } catch(JDBCException se) { + log.warn("Exception while trying to close resultset for index meta data",se); + } + } + } + + // mark columns that are unique TODO: multiple columns are not unique on their own. + Iterator>> uniqueColumnIterator = uniqueColumns.entrySet().iterator(); + while (uniqueColumnIterator.hasNext() ) { + Entry> entry = uniqueColumnIterator.next(); + Column col = entry.getKey(); + Iterator keys = entry.getValue().iterator(); + while (keys.hasNext() ) { + UniqueKey key = keys.next(); + + if(key.getColumnSpan()==1) { + col.setUnique(true); + } + } + } + + Iterator> iterator = uniquekeys.entrySet().iterator(); + while(iterator.hasNext()) { + // if keyset has no overlaps with primary key (table.getPrimaryKey()) + // if only key matches then mark as setNaturalId(true); + iterator.next(); + } + } + + private static String getCatalogForDBLookup(String catalog, String defaultCatalog) { + return catalog==null?defaultCatalog:catalog; + } + + private static String getSchemaForDBLookup(String schema, String defaultSchema) { + return schema==null?defaultSchema:schema; + } + + private static Column getColumn(RevengDialect metaDataDialect, Table table, String columnName) { + Column column = new Column(); + column.setName(quote(columnName, metaDataDialect)); + Column existing = table.getColumn(column); + if(existing!=null) { + column = existing; + } + return column; + } + + private static String quote(String columnName, RevengDialect metaDataDialect) { + if(columnName==null) return columnName; + if(metaDataDialect.needQuote(columnName)) { + if(columnName.length()>1 && columnName.charAt(0)=='`' && columnName.charAt(columnName.length()-1)=='`') { + return columnName; // avoid double quoting + } + return "`" + columnName + "`"; + } else { + return columnName; + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/PrimaryKeyProcessor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/PrimaryKeyProcessor.java new file mode 100644 index 000000000000..fc957f8360a7 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/PrimaryKeyProcessor.java @@ -0,0 +1,183 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.reader; + +import org.hibernate.JDBCException; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.PrimaryKey; +import org.hibernate.mapping.Table; +import org.hibernate.sql.Alias; +import org.hibernate.tool.reveng.api.core.RevengDialect; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.internal.core.RevengMetadataCollector; +import org.hibernate.tool.reveng.internal.core.util.RevengUtils; +import org.jboss.logging.Logger; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class PrimaryKeyProcessor { + + private static final Logger log = Logger.getLogger(PrimaryKeyProcessor.class); + + public static void processPrimaryKey( + RevengDialect metaDataDialect, + RevengStrategy revengStrategy, + String defaultSchema, + String defaultCatalog, + RevengMetadataCollector revengMetadataCollector, + Table table) { + + List columns = new ArrayList(); + PrimaryKey key = null; + Iterator> primaryKeyIterator = null; + try { + Map primaryKeyRs = null; + primaryKeyIterator = metaDataDialect.getPrimaryKeys(getCatalogForDBLookup(table.getCatalog(), defaultCatalog), getSchemaForDBLookup(table.getSchema(), defaultSchema), table.getName() ); + + while (primaryKeyIterator.hasNext() ) { + primaryKeyRs = primaryKeyIterator.next(); + + String columnName = (String) primaryKeyRs.get("COLUMN_NAME"); + short seq = (Short) primaryKeyRs.get("KEY_SEQ"); + String name = (String) primaryKeyRs.get("PK_NAME"); + + if(key==null) { + key = new PrimaryKey(table); + key.setName(name); + if(table.getPrimaryKey()!=null) { + throw new RuntimeException(table + " already has a primary key!"); //TODO: ignore ? + } + table.setPrimaryKey(key); + } + else { + if( !(Objects.equals(name, key.getName())) && name != null && !name.equals(key.getName() ) ) { + throw new RuntimeException("Duplicate names found for primarykey. Existing name: " + key.getName() + " JDBC name: " + name + " on table " + table); + } + } + + columns.add(new Object[] {seq, columnName}); + } + } finally { + if (primaryKeyIterator!=null) { + try { + metaDataDialect.close(primaryKeyIterator); + } catch(JDBCException se) { + log.warn("Exception when closing resultset for reading primary key information",se); + } + } + } + + columns.sort((o1, o2) -> { + Short left = (Short) o1[0]; + Short right = (Short) o2[0]; + return left.compareTo(right); + }); + + List t = new ArrayList(columns.size()); + for (Object[] element : columns) { + t.add((String) element[1]); + } + + if(key==null) { + log.warn("The JDBC driver didn't report any primary key columns in " + table.getName() + ". Asking rev.eng. strategy" ); + List userPrimaryKey = RevengUtils.getPrimaryKeyInfoInRevengStrategy(revengStrategy, table, defaultCatalog, defaultSchema); if(userPrimaryKey!=null && !userPrimaryKey.isEmpty()) { + key = new PrimaryKey(table); + key.setName(new Alias(15, "PK").toAliasString( table.getName())); + if(table.getPrimaryKey()!=null) { + throw new RuntimeException(table + " already has a primary key!"); //TODO: ignore ? + } + table.setPrimaryKey(key); + t = new ArrayList(userPrimaryKey); + } else { + log.warn("Rev.eng. strategy did not report any primary key columns for " + table.getName()); + } + } + + Iterator> suggestedPrimaryKeyStrategyName = metaDataDialect.getSuggestedPrimaryKeyStrategyName( getCatalogForDBLookup(table.getCatalog(), defaultCatalog), getSchemaForDBLookup(table.getSchema(), defaultSchema), table.getName() ); + try { + if(suggestedPrimaryKeyStrategyName.hasNext()) { + Map m = suggestedPrimaryKeyStrategyName.next(); + String suggestion = (String) m.get( "HIBERNATE_STRATEGY" ); + if(suggestion!=null) { + revengMetadataCollector.addSuggestedIdentifierStrategy( + transformForModelLookup(table.getCatalog(), defaultCatalog), + transformForModelLookup(table.getSchema(), defaultSchema), + table.getName(), + suggestion ); + } + } + } finally { + if(suggestedPrimaryKeyStrategyName!=null) { + try { + metaDataDialect.close(suggestedPrimaryKeyStrategyName); + } catch(JDBCException se) { + log.warn("Exception while closing iterator for suggested primary key strategy name",se); + } + } + } + + if(key!=null) { + for (String name : t) { + // should get column from table if it already exists! + Column col = getColumn(metaDataDialect, table, name); + key.addColumn(col); + } + log.debug("primary key for " + table + " -> " + key); + } + + } + + private static String getCatalogForDBLookup(String catalog, String defaultCatalog) { + return catalog==null?defaultCatalog:catalog; + } + + private static String transformForModelLookup(String id, String defaultId) { + return id == null || id.equals(defaultId) ? null : id; + } + + private static String getSchemaForDBLookup(String schema, String defaultSchema) { + return schema==null?defaultSchema:schema; + } + + private static Column getColumn(RevengDialect metaDataDialect, Table table, String columnName) { + Column column = new Column(); + column.setName(quote(metaDataDialect, columnName)); + Column existing = table.getColumn(column); + if(existing!=null) { + column = existing; + } + return column; + } + + private static String quote(RevengDialect metaDataDialect, String columnName) { + if(columnName==null) return columnName; + if(metaDataDialect.needQuote(columnName)) { + if(columnName.length()>1 && columnName.charAt(0)=='`' && columnName.charAt(columnName.length()-1)=='`') { + return columnName; // avoid double quoting + } + return "`" + columnName + "`"; + } else { + return columnName; + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/TableCollector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/TableCollector.java new file mode 100644 index 000000000000..1e150cf9f52a --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/reader/TableCollector.java @@ -0,0 +1,161 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.reader; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.RevengDialect; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.RevengStrategy.SchemaSelection; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.core.RevengMetadataCollector; +import org.jboss.logging.Logger; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +public class TableCollector { + + private static final Logger log = Logger.getLogger(TableCollector.class); + + public static TableCollector create( + RevengDialect metaDataDialect, + RevengStrategy revengStrategy, + RevengMetadataCollector revengMetadataCollector, + Properties properties) { + return new TableCollector( + metaDataDialect, + revengStrategy, + revengMetadataCollector, + properties); + } + + private RevengDialect metaDataDialect; + private RevengStrategy revengStrategy; + private RevengMetadataCollector revengMetadataCollector; + private Properties properties; + + private TableCollector( + RevengDialect metaDataDialect, + RevengStrategy revengStrategy, + RevengMetadataCollector revengMetadataCollector, + Properties properties) { + this.metaDataDialect = metaDataDialect; + this.revengStrategy = revengStrategy; + this.revengMetadataCollector = revengMetadataCollector; + this.properties = properties; + } + + public Map processTables(SchemaSelection schemaSelection) { + Iterator> tableIterator = null; + HashMap processedTables = new HashMap(); + try { + tableIterator = metaDataDialect.getTables( + StringHelper.replace(schemaSelection.getMatchCatalog(),".*", "%"), + StringHelper.replace(schemaSelection.getMatchSchema(),".*", "%"), + StringHelper.replace(schemaSelection.getMatchTable(),".*", "%")); + while (tableIterator.hasNext() ) { + processTable(tableIterator.next(), processedTables); + } + } + finally { + if (tableIterator!=null) { + metaDataDialect.close(tableIterator); + } + } + return processedTables; + } + + private void processTable(Map tableRs, HashMap processedTables) { + TableIdentifier tableIdentifier = TableIdentifier.create( + quote((String) tableRs.get("TABLE_CAT")), + quote((String) tableRs.get("TABLE_SCHEM")), + quote((String) tableRs.get("TABLE_NAME"))); + if(revengStrategy.excludeTable(tableIdentifier) ) { + log.debug("Table " + tableIdentifier + " excluded by strategy"); + } else if (revengMetadataCollector.getTable(tableIdentifier)!=null) { + log.debug("Ignoring " + tableIdentifier + " since it has already been processed"); + } else { + addTable( + tableIdentifier, + (String) tableRs.get("TABLE_TYPE"), + (String) tableRs.get("REMARKS"), + processedTables); + } + } + + private void addTable( + TableIdentifier tableIdentifier, + String tableType, + String comment, + HashMap processedTables) { + if (isTypeToAdd(tableType)) { //|| + log.debug("Adding table " + tableIdentifier + " of type " + tableType); + Table table = revengMetadataCollector.addTable(tableIdentifier); + table.setComment(comment); + BasicColumnProcessor.processBasicColumns( + metaDataDialect, + revengStrategy, + properties.getProperty(AvailableSettings.DEFAULT_SCHEMA), + properties.getProperty(AvailableSettings.DEFAULT_CATALOG), + table); + PrimaryKeyProcessor.processPrimaryKey( + metaDataDialect, + revengStrategy, + properties.getProperty(AvailableSettings.DEFAULT_SCHEMA), + properties.getProperty(AvailableSettings.DEFAULT_CATALOG), + revengMetadataCollector, + table); + if (tableType.equalsIgnoreCase("TABLE")) { + IndexProcessor.processIndices( + metaDataDialect, + properties.getProperty(AvailableSettings.DEFAULT_SCHEMA), + properties.getProperty(AvailableSettings.DEFAULT_CATALOG), + table); + } + processedTables.put(table, tableType.equalsIgnoreCase("TABLE")); + } + else { + log.debug("Ignoring table " + tableIdentifier + " of type " + tableType); + } + } + + private boolean isTypeToAdd(String tableType) { + return "TABLE".equalsIgnoreCase(tableType) || + "VIEW".equalsIgnoreCase(tableType) || + "SYNONYM".equals(tableType); + } + + + private String quote(String name) { + if (name == null) + return name; + if (metaDataDialect.needQuote(name)) { + if (name.length() > 1 && name.charAt(0) == '`' + && name.charAt(name.length() - 1) == '`') { + return name; // avoid double quoting + } + return "`" + name + "`"; + } else { + return name; + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/AbstractStrategy.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/AbstractStrategy.java new file mode 100644 index 000000000000..e28b2739b70c --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/AbstractStrategy.java @@ -0,0 +1,354 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.strategy; + +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.MetaAttribute; +import org.hibernate.mapping.PrimaryKey; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.AssociationInfo; +import org.hibernate.tool.reveng.api.core.RevengSettings; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.util.JdbcToHibernateTypeHelper; +import org.hibernate.tool.reveng.internal.util.NameConverter; +import org.hibernate.tool.reveng.internal.util.TableNameQualifier; +import org.jboss.logging.Logger; + +import java.beans.Introspector; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +public abstract class AbstractStrategy implements RevengStrategy { + + static final private Logger log = Logger.getLogger(AbstractStrategy.class); + + private static final Set AUTO_OPTIMISTICLOCK_COLUMNS; + + private RevengSettings settings = new RevengSettings(this); + + static { + AUTO_OPTIMISTICLOCK_COLUMNS = new HashSet(); + AUTO_OPTIMISTICLOCK_COLUMNS.add("version"); + AUTO_OPTIMISTICLOCK_COLUMNS.add("timestamp"); + AUTO_OPTIMISTICLOCK_COLUMNS.add("dbtimestamp"); + } + + + public AbstractStrategy() { + super(); + } + + public String columnToPropertyName(TableIdentifier table, String columnName) { + String decapitalize = Introspector.decapitalize( toUpperCamelCase(columnName) ); + + return keywordCheck( decapitalize ); + } + + private String keywordCheck(String possibleKeyword) { + if(NameConverter.isReservedJavaKeyword(possibleKeyword)) { + possibleKeyword = possibleKeyword + "_"; + } + return possibleKeyword; + } + + protected String toUpperCamelCase(String s) { + return NameConverter.toUpperCamelCase(s); + } + + /** + * Does some crude english pluralization + * TODO: are the from/to names correct ? + */ + public String foreignKeyToCollectionName(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns, boolean uniqueReference) { + String propertyName = Introspector.decapitalize( StringHelper.unqualify( getRoot().tableToClassName(fromTable) ) ); + propertyName = pluralize( propertyName ); + + if(!uniqueReference) { + if(fromColumns!=null && fromColumns.size()==1) { + String columnName = ( (Column) fromColumns.get(0) ).getName(); + propertyName = propertyName + "For" + toUpperCamelCase(columnName); + } + else { // composite key or no columns at all safeguard + propertyName = propertyName + "For" + toUpperCamelCase(keyname); + } + } + return propertyName; + } + + protected String pluralize(String singular) { + return NameConverter.simplePluralize(singular); + } + + public String foreignKeyToInverseEntityName(String keyname, + TableIdentifier fromTable, List fromColumnNames, + TableIdentifier referencedTable, List referencedColumnNames, + boolean uniqueReference) { + return foreignKeyToEntityName(keyname, fromTable, fromColumnNames, referencedTable, referencedColumnNames, uniqueReference); + } + + + public String foreignKeyToEntityName(String keyname, TableIdentifier fromTable, List fromColumnNames, TableIdentifier referencedTable, List referencedColumnNames, boolean uniqueReference) { + String propertyName = Introspector.decapitalize( StringHelper.unqualify( getRoot().tableToClassName(referencedTable) ) ); + + if(!uniqueReference) { + if(fromColumnNames!=null && fromColumnNames.size()==1) { + String columnName = ( (Column) fromColumnNames.get(0) ).getName(); + propertyName = propertyName + "By" + toUpperCamelCase(columnName); + } + else { // composite key or no columns at all safeguard + propertyName = propertyName + "By" + toUpperCamelCase(keyname); + } + } + + return propertyName; + } + + public String columnToHibernateTypeName(TableIdentifier table, String columnName, int sqlType, int length, int precision, int scale, boolean nullable, boolean generatedIdentifier) { + String preferredHibernateType = JdbcToHibernateTypeHelper.getPreferredHibernateType(sqlType, length, precision, scale, nullable, generatedIdentifier); + + String location = ""; + if(log.isDebugEnabled()) { + String info = " t:" + JdbcToHibernateTypeHelper.getJDBCTypeName( sqlType ) + " l:" + length + " p:" + precision + " s:" + scale + " n:" + nullable + " id:" + generatedIdentifier; + if(table!=null) { + location = TableNameQualifier.qualify(table.getCatalog(), table.getSchema(), table.getName() ) + "." + columnName + info; + } else { + + location += " Column: " + columnName + info; + } + } + if(preferredHibernateType==null) { + log.debug("No default type found for [" + location + "] falling back to [serializable]"); + return "serializable"; + } else { + log.debug("Default type found for [" + location + "] to [" + preferredHibernateType + "]"); + return preferredHibernateType; + } + } + + public boolean excludeTable(TableIdentifier ti) { + return false; + } + + public boolean excludeColumn(TableIdentifier identifier, String columnName) { + return false; + } + + public String tableToClassName(TableIdentifier tableIdentifier) { + + String pkgName = settings.getDefaultPackageName(); + String className = toUpperCamelCase( tableIdentifier.getName() ); + + if(!pkgName.isEmpty()) { + return StringHelper.qualify(pkgName, className); + } + else { + return className; + } + + } + + public List getForeignKeys(TableIdentifier referencedTable) { + return Collections.emptyList(); + } + + public String getTableIdentifierStrategyName(TableIdentifier identifier) { + return null; + } + + public Properties getTableIdentifierProperties(TableIdentifier identifier) { + return new Properties(); + } + + public List getPrimaryKeyColumnNames(TableIdentifier identifier) { + return null; + } + + public String classNameToCompositeIdName(String className) { + return className + "Id"; + } + + public void close() { + + } + + + + /** Return explicit which column name should be used for optimistic lock */ + public String getOptimisticLockColumnName(TableIdentifier identifier) { + return null; + } + + public boolean useColumnForOptimisticLock(TableIdentifier identifier, String column) { + if(settings.getDetectOptimsticLock()) { + return AUTO_OPTIMISTICLOCK_COLUMNS.contains(column.toLowerCase()); + } else { + return false; + } + } + + public List getSchemaSelections() { + return null; + } + + public String tableToIdentifierPropertyName(TableIdentifier tableIdentifier) { + return null; + } + + public String tableToCompositeIdName(TableIdentifier identifier) { + return null; + } + + public boolean excludeForeignKeyAsCollection(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns) { + return !settings.createCollectionForForeignKey(); + } + + public boolean excludeForeignKeyAsManytoOne(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns) { + return !settings.createManyToOneForForeignKey(); + } + + public boolean isForeignKeyCollectionInverse(String name, Table foreignKeyTable, List columns, Table foreignKeyReferencedTable, List referencedColumns) { + if(foreignKeyTable==null) { + return true; // we don't know better + } + if(isManyToManyTable(foreignKeyTable)) { + // if the reference column is the first one then we are inverse. + Column column = foreignKeyTable.getColumn(0); + Column fkColumn = (Column) referencedColumns.get(0); + return fkColumn.equals(column); + } + return true; + } + + public boolean isForeignKeyCollectionLazy(String name, TableIdentifier foreignKeyTable, List columns, TableIdentifier foreignKeyReferencedTable, List referencedColumns) { + return true; + } + + public void setSettings(RevengSettings settings) { + this.settings = settings; + } + + public boolean isOneToOne(ForeignKey foreignKey) { + if(settings.getDetectOneToOne()) { + // add support for non-PK associations + List fkColumns = foreignKey.getColumns(); + List pkForeignTableColumns = Collections.emptyList(); + + if (foreignKey.getTable().hasPrimaryKey()) + pkForeignTableColumns = foreignKey.getTable().getPrimaryKey().getColumns(); + + boolean equals = fkColumns.size() == pkForeignTableColumns.size(); + + Iterator columns = foreignKey.getColumns().iterator(); + while (equals && columns.hasNext()) { + Column fkColumn = columns.next(); + equals = pkForeignTableColumns.contains(fkColumn); + } + + return equals; + } else { + return false; + } + } + + public boolean isManyToManyTable(Table table) { + if(settings.getDetectManyToMany()) { + + // if the number of columns in the primary key is different + // than the total number of columns then it can't be a middle table + PrimaryKey pk = table.getPrimaryKey(); + if ( pk==null || pk.getColumns().size() != table.getColumnSpan() ) + return false; + + List foreignKeys = new ArrayList(); + + // if we have more than 2 fk, means we have more than 2 table implied + // in this table --> cannot be a simple many-to-many + for (ForeignKey fkey : table.getForeignKeyCollection()) { + foreignKeys.add( fkey ); + if(foreignKeys.size()>2) { + return false; // early exit if we have more than two fk. + } + } + if(foreignKeys.size()!=2) { + return false; + } + + // tests that all columns are implied in the fks + Set columns = new HashSet<>(table.getColumns()); + + for (ForeignKey fkey : table.getForeignKeyCollection()) { + if (columns.isEmpty()) break; + fkey.getColumns().forEach(columns::remove); + } + + return columns.isEmpty(); + + } else { + return false; + } + } + + protected RevengStrategy getRoot() { + return settings.getRootStrategy(); + } + + public String foreignKeyToManyToManyName(ForeignKey fromKey, TableIdentifier middleTable, ForeignKey toKey, boolean uniqueReference) { + String propertyName = Introspector.decapitalize( StringHelper.unqualify( getRoot().tableToClassName(TableIdentifier.create( toKey.getReferencedTable()) )) ); + propertyName = pluralize( propertyName ); + + if(!uniqueReference) { + //TODO: maybe use the middleTable name here ? + if(toKey.getColumns()!=null && toKey.getColumns().size()==1) { + String columnName = ( (Column) toKey.getColumns().get(0) ).getName(); + propertyName = propertyName + "For" + toUpperCamelCase(columnName); + } + else { // composite key or no columns at all safeguard + propertyName = propertyName + "For" + toUpperCamelCase(toKey.getName()); + } + } + return propertyName; + } + + public Map tableToMetaAttributes(TableIdentifier tableIdentifier) { + return null; + } + + public Map columnToMetaAttributes(TableIdentifier identifier, String column) { + return null; + } + + public AssociationInfo foreignKeyToAssociationInfo(ForeignKey foreignKey) { + return null; + } + + public AssociationInfo foreignKeyToInverseAssociationInfo(ForeignKey foreignKey) { + return null; + } + + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/DefaultStrategy.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/DefaultStrategy.java new file mode 100644 index 000000000000..fc49b863d177 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/DefaultStrategy.java @@ -0,0 +1,24 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2020-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.strategy; + +import org.hibernate.tool.reveng.api.core.RevengStrategy; + +public class DefaultStrategy extends AbstractStrategy implements RevengStrategy { + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/DelegatingStrategy.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/DelegatingStrategy.java new file mode 100644 index 000000000000..e570b5878149 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/DelegatingStrategy.java @@ -0,0 +1,175 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.strategy; + +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.MetaAttribute; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.AssociationInfo; +import org.hibernate.tool.reveng.api.core.RevengSettings; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.TableIdentifier; + +import java.util.List; +import java.util.Map; +import java.util.Properties; + +public class DelegatingStrategy implements RevengStrategy { + + RevengStrategy delegate; + + public List getForeignKeys(TableIdentifier referencedTable) { + return delegate==null?null:delegate.getForeignKeys(referencedTable); + } + + public DelegatingStrategy(RevengStrategy delegate) { + this.delegate = delegate; + } + + public String columnToPropertyName(TableIdentifier table, String column) { + return delegate==null?null:delegate.columnToPropertyName(table, column); + } + + public boolean excludeTable(TableIdentifier ti) { + return delegate==null?false:delegate.excludeTable(ti); + } + + public boolean excludeColumn(TableIdentifier identifier, String columnName) { + return delegate==null?false:delegate.excludeColumn(identifier, columnName); + } + + public String foreignKeyToCollectionName(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns, boolean uniqueReference) { + return delegate==null?null:delegate.foreignKeyToCollectionName(keyname, fromTable, fromColumns, referencedTable, referencedColumns, uniqueReference); + } + + public String foreignKeyToEntityName(String keyname, TableIdentifier fromTable, List fromColumnNames, TableIdentifier referencedTable, List referencedColumnNames, boolean uniqueReference) { + return delegate==null?null:delegate.foreignKeyToEntityName(keyname, fromTable, fromColumnNames, referencedTable, referencedColumnNames, uniqueReference); + } + + public String columnToHibernateTypeName(TableIdentifier table, String columnName, int sqlType, int length, int precision, int scale, boolean nullable, boolean generatedIdentifier) { + return delegate==null?null:delegate.columnToHibernateTypeName(table, columnName, sqlType, length, precision, scale, nullable, generatedIdentifier); + } + + public String tableToClassName(TableIdentifier tableIdentifier) { + return delegate==null?null:delegate.tableToClassName(tableIdentifier); + } + + public String getTableIdentifierStrategyName(TableIdentifier tableIdentifier) { + return delegate==null?null:delegate.getTableIdentifierStrategyName(tableIdentifier); + } + + public Properties getTableIdentifierProperties(TableIdentifier identifier) { + return delegate==null?null:delegate.getTableIdentifierProperties(identifier); + } + + public List getPrimaryKeyColumnNames(TableIdentifier identifier) { + return delegate==null?null:delegate.getPrimaryKeyColumnNames(identifier); + } + + public String classNameToCompositeIdName(String className) { + return delegate==null?null:delegate.classNameToCompositeIdName(className); + } + + public void close() { + if(delegate!=null) delegate.close(); + } + + public String getOptimisticLockColumnName(TableIdentifier identifier) { + return delegate==null?null:delegate.getOptimisticLockColumnName(identifier); + } + + public boolean useColumnForOptimisticLock(TableIdentifier identifier, String column) { + return delegate==null?false:delegate.useColumnForOptimisticLock(identifier, column); + } + + public List getSchemaSelections() { + return delegate==null?null:delegate.getSchemaSelections(); + } + + public String tableToIdentifierPropertyName(TableIdentifier tableIdentifier) { + return delegate==null?null:delegate.tableToIdentifierPropertyName(tableIdentifier); + } + + public String tableToCompositeIdName(TableIdentifier identifier) { + return delegate==null?null:delegate.tableToCompositeIdName(identifier); + } + + public boolean excludeForeignKeyAsCollection(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns) { + return delegate==null?false:delegate.excludeForeignKeyAsCollection(keyname, fromTable, fromColumns, referencedTable, referencedColumns); + } + + public boolean excludeForeignKeyAsManytoOne(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns) { + return delegate==null?false:delegate.excludeForeignKeyAsManytoOne(keyname, fromTable, fromColumns, referencedTable, referencedColumns); + } + + public boolean isForeignKeyCollectionInverse(String name, Table foreignKeyTable, List columns, Table foreignKeyReferencedTable, List referencedColumns) { + return delegate==null?true:delegate.isForeignKeyCollectionInverse(name, foreignKeyTable, columns, foreignKeyReferencedTable, referencedColumns); + } + + public boolean isForeignKeyCollectionLazy(String name, TableIdentifier foreignKeyTable, List columns, TableIdentifier foreignKeyReferencedTable, List referencedColumns) { + return delegate==null?true:delegate.isForeignKeyCollectionLazy(name, foreignKeyTable, columns, foreignKeyReferencedTable, referencedColumns); + } + + /** + * Initialize the settings. + * + * If subclasses need to use the Settings then it should keep its own reference, but still remember to initialize the delegates settings by calling super.setSettings(settings). + * + * @see RevengStrategy.setSettings + */ + public void setSettings(RevengSettings settings) { + if(delegate!=null) delegate.setSettings(settings); + } + + public boolean isManyToManyTable(Table table) { + return delegate==null?true:delegate.isManyToManyTable( table ); + } + + public boolean isOneToOne(ForeignKey foreignKey) { + return delegate==null?true:delegate.isOneToOne( foreignKey ); + } + + + public String foreignKeyToManyToManyName(ForeignKey fromKey, TableIdentifier middleTable, ForeignKey toKey, boolean uniqueReference) { + return delegate==null?null:delegate.foreignKeyToManyToManyName( fromKey, middleTable, toKey, uniqueReference ); + } + + public Map tableToMetaAttributes(TableIdentifier tableIdentifier) { + return delegate==null?null:delegate.tableToMetaAttributes( tableIdentifier ); + } + + public Map columnToMetaAttributes(TableIdentifier identifier, String column) { + return delegate==null?null:delegate.columnToMetaAttributes( identifier, column ); + } + + public AssociationInfo foreignKeyToAssociationInfo(ForeignKey foreignKey) { + return delegate==null?null:delegate.foreignKeyToAssociationInfo(foreignKey); + } + + public AssociationInfo foreignKeyToInverseAssociationInfo(ForeignKey foreignKey) { + return delegate==null?null:delegate.foreignKeyToInverseAssociationInfo(foreignKey); + } + + public String foreignKeyToInverseEntityName(String keyname, + TableIdentifier fromTable, List fromColumnNames, + TableIdentifier referencedTable, List referencedColumnNames, + boolean uniqueReference) { + return delegate==null?null:delegate.foreignKeyToInverseEntityName(keyname, fromTable, fromColumnNames, referencedTable, referencedColumnNames, uniqueReference); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/MetaAttributeHelper.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/MetaAttributeHelper.java new file mode 100644 index 000000000000..52d879bce3b2 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/MetaAttributeHelper.java @@ -0,0 +1,148 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.strategy; + +import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.multimap.HashSetValuedHashMap; +import org.hibernate.mapping.MetaAttribute; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +public class MetaAttributeHelper { + + public static MetaAttribute toRealMetaAttribute(String name, Collection values) { + MetaAttribute attribute = new MetaAttribute(name); + for (Iterator iter = values.iterator(); iter.hasNext();) { + SimpleMetaAttribute element = (SimpleMetaAttribute) iter.next(); + attribute.addValue(element.value); + } + + return attribute; + } + + + public static MultiValuedMap loadAndMergeMetaMap( + Element classElement, + MultiValuedMap inheritedMeta) { + return MetaAttributeHelper.mergeMetaMaps( + loadMetaMap(classElement), + inheritedMeta); + } + + public static MultiValuedMap loadMetaMap(Element element) { + MultiValuedMap result = new HashSetValuedHashMap(); + List metaAttributeList = new ArrayList(); + ArrayList metaNodes = getChildElements(element, "meta"); + for (Element metaNode : metaNodes) { + metaAttributeList.add(metaNode); + } + for (Iterator iter = metaAttributeList.iterator(); iter.hasNext();) { + Element metaAttribute = iter.next(); + String attribute = metaAttribute.getAttribute("attribute"); + String value = metaAttribute.getTextContent(); + String inheritStr= null; + if (metaAttribute.hasAttribute("inherit")) { + inheritStr = metaAttribute.getAttribute("inherit"); + } + boolean inherit = true; + if(inheritStr!=null) { + inherit = Boolean.valueOf(inheritStr).booleanValue(); + } + SimpleMetaAttribute ma = new SimpleMetaAttribute(value, inherit); + result.put(attribute, ma); + } + return result; + } + + /** + * Merges a Multimap with inherited maps. + * Values specified always overrules/replaces the inherited values. + * + * @param specific + * @param general + * @return a MultiMap with all values from local and extra values + * from inherited + */ + private static MultiValuedMap mergeMetaMaps( + MultiValuedMap specific, + MultiValuedMap general) { + MultiValuedMap result = new HashSetValuedHashMap(); + copyMultiMap(result, specific); + if (general != null) { + for (Iterator iter = general.keySet().iterator();iter.hasNext();) { + String key = iter.next(); + if (!specific.containsKey(key) ) { + // inheriting a meta attribute only if it is inheritable + Collection ml = general.get(key); + for (Iterator iterator = ml.iterator(); iterator.hasNext();) { + SimpleMetaAttribute element = iterator.next(); + if (element.inheritable) { + result.put(key, element); + } + } + } + } + } + + return result; + + } + + private static void copyMultiMap( + MultiValuedMap destination, + MultiValuedMap specific) { + for (Iterator keyIterator = specific.keySet().iterator(); keyIterator.hasNext(); ) { + String key = keyIterator.next(); + Collection c = specific.get(key); + for (Iterator valueIterator = c.iterator(); valueIterator.hasNext(); ) + destination.put(key, valueIterator.next() ); + } + } + + private static ArrayList getChildElements(Element parent, String tagName) { + ArrayList result = new ArrayList(); + NodeList nodeList = parent.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + if (node instanceof Element) { + if (tagName.equals(((Element)node).getTagName())) { + result.add((Element)node); + } + } + } + return result; + } + + public static class SimpleMetaAttribute { + String value; + boolean inheritable = true; + public SimpleMetaAttribute(String value, boolean inherit) { + this.value = value; + this.inheritable = inherit; + } + public String toString() { + return value; + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/OverrideBinder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/OverrideBinder.java new file mode 100644 index 000000000000..28b887d98483 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/OverrideBinder.java @@ -0,0 +1,479 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.strategy; + +import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.multimap.HashSetValuedHashMap; +import org.hibernate.MappingException; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.AssociationInfo; +import org.hibernate.tool.reveng.api.core.RevengStrategy.SchemaSelection; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.core.strategy.MetaAttributeHelper.SimpleMetaAttribute; +import org.hibernate.tool.reveng.internal.core.util.RevengUtils; +import org.hibernate.tool.reveng.internal.util.JdbcToHibernateTypeHelper; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +public class OverrideBinder { + + public static void bindRoot(OverrideRepository repository, Document doc) { + Element rootElement = doc.getDocumentElement(); + bindSchemaSelections(getChildElements(rootElement, "schema-selection"), repository); + bindTypeMappings(getChildElements(rootElement, "type-mapping"), repository); + bindTableFilters(getChildElements(rootElement, "table-filter"), repository); + bindTables(getChildElements(rootElement, "table"), repository); + } + + private static void bindSchemaSelections( + ArrayList schemaSelections, + OverrideRepository repository) { + for (Element schemaSelection : schemaSelections) { + bindSchemaSelection(schemaSelection, repository); + } + } + + private static void bindTypeMappings( + ArrayList typeMappings, + OverrideRepository repository) { + if ( !typeMappings.isEmpty() ) { + bindTypeMapping(typeMappings.get(0), repository); + } + } + + private static void bindTableFilters( + ArrayList tableFilters, + OverrideRepository repository) { + for (Element element : tableFilters) { + TableFilter tableFilter = new TableFilter(); + tableFilter.setMatchCatalog(getAttribute(element, "match-catalog")); + tableFilter.setMatchSchema(getAttribute(element, "match-schema")); + tableFilter.setMatchName(getAttribute(element, "match-name")); + tableFilter.setExclude(Boolean.valueOf(getAttribute(element, "exclude"))); + tableFilter.setPackage(getAttribute(element, "package")); + MultiValuedMap map = + MetaAttributeHelper.loadAndMergeMetaMap( + element, + new HashSetValuedHashMap()); + if ( !map.isEmpty() ) { + tableFilter.setMetaAttributes(map); + } else { + tableFilter.setMetaAttributes(null); + } + repository.addTableFilter(tableFilter); + } + } + + private static void bindTables( + ArrayList tables, + OverrideRepository repository) { + for (Element element : tables) { + Table table = new Table("Hibernate Tools"); + table.setCatalog(getAttribute(element, "catalog")); + table.setSchema(getAttribute(element, "schema")); + table.setName(getAttribute(element, "name")); + ArrayList primaryKeys = getChildElements(element, "primary-key"); + if ( !primaryKeys.isEmpty() ) { + bindPrimaryKey(primaryKeys.get(0), table, repository); + } + bindColumns(getChildElements(element, "column"), table, repository); + bindForeignKeys(getChildElements(element, "foreign-key"), table, repository); + bindMetaAttributes(element, table, repository); + repository.addTable(table, getAttribute(element, "class")); + } + } + + private static void bindPrimaryKey( + Element element, + Table table, + OverrideRepository repository) { + String propertyName = getAttribute(element, "property"); + String compositeIdName = getAttribute(element, "id-class"); + ArrayList generators = getChildElements(element, "generator"); + if ( !generators.isEmpty() ) { + Element generator = generators.get(0); + String identifierClass = getAttribute(generator, "class"); + Properties params = new Properties(); + ArrayList parameterList = getChildElements(generator, "param"); + for ( Element parameter : parameterList ) { + params.setProperty( getAttribute( parameter, "name" ), parameter.getTextContent() ); + } + repository.addTableIdentifierStrategy(table, identifierClass, params); + } + List boundColumnNames = bindColumns(getChildElements(element, "key-column"), table, repository); + repository.addPrimaryKeyNamesForTable(table, boundColumnNames, propertyName, compositeIdName); + } + + private static List bindColumns( + ArrayList columns, + Table table, + OverrideRepository repository) { + List columnNames = new ArrayList(); + for (Element element : columns) { + Column column = new Column(); + column.setName(getAttribute(element, "name")); + String attributeValue = getAttribute(element, "jdbc-type"); + if (StringHelper.isNotEmpty(attributeValue)) { + column.setSqlTypeCode( JdbcToHibernateTypeHelper.getJDBCType( attributeValue ) ); + } + TableIdentifier tableIdentifier = TableIdentifier.create(table); + if (table.getColumn(column) != null) { + throw new MappingException("Column " + column.getName() + " already exists in table " + tableIdentifier ); + } + MultiValuedMap map = + MetaAttributeHelper.loadAndMergeMetaMap( + element, + new HashSetValuedHashMap()); + if( !map.isEmpty() ) { + repository.addMetaAttributeInfo( tableIdentifier, column.getName(), map); + } + table.addColumn(column); + columnNames.add(column.getName()); + repository.setTypeNameForColumn( + tableIdentifier, + column.getName(), + getAttribute(element, "type")); + repository.setPropertyNameForColumn( + tableIdentifier, + column.getName(), + getAttribute(element, "property")); + boolean excluded = Boolean.parseBoolean(element.getAttribute("exclude") ); + if(excluded) { + repository.setExcludedColumn(tableIdentifier, column.getName()); + } + if (element.hasAttribute("foreign-table")) { + String foreignTableName = element.getAttribute("foreign-table"); + List localColumns = new ArrayList(); + localColumns.add(column); + List foreignColumns = new ArrayList(); + Table foreignTable = new Table("Hibernate Tools"); + foreignTable.setName(foreignTableName); + foreignTable.setCatalog( + element.hasAttribute("foreign-catalog") ? + element.getAttribute("foreign-catalog") : + table.getCatalog()); + foreignTable.setSchema( + element.hasAttribute("foreign-schema") ? + element.getAttribute("foreign-schema") : + table.getSchema()); + if (element.hasAttribute("foreign-column")) { + String foreignColumnName = element.getAttribute("foreign-column"); + Column foreignColumn = new Column(); + foreignColumn.setName(foreignColumnName); + foreignColumns.add(foreignColumn); + } else { + throw new MappingException("foreign-column is required when foreign-table is specified on " + column); + } + ForeignKey key = table.createForeignKey( + null, + localColumns, + foreignTableName, + null, + null, + foreignColumns); + key.setReferencedTable(foreignTable); // only possible if foreignColumns is explicitly specified (workaround on aligncolumns) + } + } + return columnNames; + } + + private static void bindForeignKeys( + ArrayList foreignKeys, + Table table, + OverrideRepository repository) { + for (Element element : foreignKeys) { + String constraintName = getAttribute(element, "constraint-name"); + String foreignTableName = getAttribute(element, "foreign-table"); + if (foreignTableName != null) { + Table foreignTable = new Table("hibernate tools"); + foreignTable.setName(foreignTableName); + foreignTable.setCatalog( + element.hasAttribute("foreign-catalog") ? + element.getAttribute("foreign-catalog") : + table.getCatalog()); + foreignTable.setSchema( + element.hasAttribute("foreign-schema") ? + element.getAttribute("foreign-schema") : + table.getSchema()); + List localColumns = new ArrayList(); + List foreignColumns = new ArrayList(); + ArrayList columnRefs = getChildElements(element, "column-ref"); + for (Element columnRef : columnRefs) { + localColumns.add(new Column(columnRef.getAttribute("local-column"))); + foreignColumns.add(new Column(columnRef.getAttribute("foreign-column"))); + } + ForeignKey key = table.createForeignKey( + constraintName, + localColumns, + foreignTableName, + null, + null, + foreignColumns); + key.setReferencedTable(foreignTable); // only possible if foreignColumns is explicitly specified (workaround on aligncolumns) + } + if (StringHelper.isNotEmpty(constraintName)) { + if (!validateFkAssociations(element)) { + throw new IllegalArgumentException("you can't mix or with <(inverse-)one-to-one/> "); + } + if (!bindManyToOneAndCollection(element, constraintName, repository)) { + bindOneToOne(element, constraintName, repository); + } + } + + } + } + + private static void bindOneToOne(Element element, String constraintName, + OverrideRepository repository) { + String oneToOneProperty = null; + Boolean excludeOneToOne = null; + ArrayList oneToOnes = getChildElements(element, "one-to-one"); + Element oneToOne = null; + AssociationInfo associationInfo = null; + if( !oneToOnes.isEmpty() ) { + oneToOne = oneToOnes.get(0); + oneToOneProperty = getAttribute(oneToOne, "property"); + excludeOneToOne = Boolean.valueOf(oneToOne.getAttribute("exclude")); + associationInfo = extractAssociationInfo(oneToOne); + } + + String inverseOneToOneProperty = null; + Boolean excludeInverseOneToOne = null; + ArrayList inverseOneToOnes = getChildElements(element, "inverse-one-to-one"); + Element inverseOneToOne = null; + AssociationInfo inverseAssociationInfo = null; + if( !inverseOneToOnes.isEmpty() ) { + inverseOneToOne = inverseOneToOnes.get(0); + inverseOneToOneProperty = getAttribute(inverseOneToOne, "property"); + excludeInverseOneToOne = Boolean.valueOf(inverseOneToOne.getAttribute("exclude")); + inverseAssociationInfo = extractAssociationInfo(inverseOneToOne); + } + // having oneToOne = null and inverseOneToOne != null doesn't make sense + // we cannot have the inverse side without the owning side in this case + if ( (oneToOne!=null) ) { + repository.addForeignKeyInfo( + constraintName, + oneToOneProperty, + excludeOneToOne, + inverseOneToOneProperty, + excludeInverseOneToOne, + associationInfo, + inverseAssociationInfo); + } + } + + private static boolean bindManyToOneAndCollection( + Element element, + String constraintName, + OverrideRepository repository) { + String manyToOneProperty = null; + Boolean excludeManyToOne = null; + AssociationInfo associationInfo = null; + AssociationInfo inverseAssociationInfo = null; + ArrayList manyToOnes = getChildElements(element, "many-to-one"); + Element manyToOne = null; + if ( !manyToOnes.isEmpty() ) { + manyToOne = manyToOnes.get(0); + manyToOneProperty = getAttribute(manyToOne, "property"); + excludeManyToOne = Boolean.valueOf(manyToOne.getAttribute("exclude")); + associationInfo = extractAssociationInfo(manyToOne); + } + String collectionProperty = null; + Boolean excludeCollection = null; + ArrayList sets = getChildElements(element, "set"); + Element set = null; + if ( !sets.isEmpty() ) { + set = sets.get(0); + collectionProperty = getAttribute(set, "property"); + excludeCollection = Boolean.valueOf(set.getAttribute("exclude")); + inverseAssociationInfo = extractAssociationInfo(set); + } + if ( (manyToOne!=null) || (set!=null) ) { + repository.addForeignKeyInfo( + constraintName, + manyToOneProperty, + excludeManyToOne, + collectionProperty, + excludeCollection, + associationInfo, + inverseAssociationInfo); + return true; + } else { + return false; + } + } + + private static AssociationInfo extractAssociationInfo(Element manyToOne) { + return RevengUtils.createAssociationInfo( + manyToOne.hasAttribute("cascade") ? manyToOne.getAttribute("cascade") : null, + manyToOne.hasAttribute("fetch") ? manyToOne.getAttribute("fetch") : null, + manyToOne.hasAttribute("insert") ? + Boolean.parseBoolean(manyToOne.getAttribute("insert")) : null, + manyToOne.hasAttribute("update") ? + Boolean.parseBoolean(manyToOne.getAttribute("update")) : null); + } + + private static boolean validateFkAssociations(Element element){ + ArrayList manyToOnes = getChildElements(element, "many-to-one"); + ArrayList oneToOnes = getChildElements(element, "one-to-one"); + ArrayList sets = getChildElements(element, "set"); + ArrayList inverseOneToOnes = getChildElements(element, "inverse-one-to-one"); + if ( !manyToOnes.isEmpty() && + (!oneToOnes.isEmpty() || !inverseOneToOnes.isEmpty())) { + return false; + } + if ( !oneToOnes.isEmpty() && !sets.isEmpty() ) { + return false; + } + return inverseOneToOnes.isEmpty() || sets.isEmpty(); + } + + private static void bindMetaAttributes( + Element element, + Table table, + OverrideRepository repository) { + MultiValuedMap map = + MetaAttributeHelper.loadAndMergeMetaMap( + element, + new HashSetValuedHashMap()); + if( !map.isEmpty() ) { + repository.addMetaAttributeInfo( table, map); + } + } + + private static void bindSchemaSelection( + Element schemaSelectionElement, + OverrideRepository repository) { + repository.addSchemaSelection( + new SchemaSelection() { + @Override + public String getMatchCatalog() { + return getAttribute(schemaSelectionElement, "match-catalog"); + } + @Override + public String getMatchSchema() { + return getAttribute(schemaSelectionElement, "match-schema"); + } + @Override + public String getMatchTable() { + return getAttribute(schemaSelectionElement, "match-table"); + } + + }); + } + + private static void bindTypeMapping( + Element typeMapping, + OverrideRepository repository) { + ArrayList sqlTypes = getChildElements(typeMapping, "sql-type"); + for ( Element sqlType : sqlTypes ) { + bindSqlType( sqlType, repository ); + } + } + + private static void bindSqlType(Element sqlType, OverrideRepository repository) { + int jdbcType = JdbcToHibernateTypeHelper.getJDBCType( + getAttribute(sqlType, "jdbc-type")); + SQLTypeMapping sqlTypeMapping = new SQLTypeMapping(jdbcType); + sqlTypeMapping.setHibernateType(getHibernateType(sqlType)); + sqlTypeMapping.setLength(getInteger( + getAttribute(sqlType, "length"), + SQLTypeMapping.UNKNOWN_LENGTH)); + sqlTypeMapping.setPrecision(getInteger( + getAttribute(sqlType, "precision"), + SQLTypeMapping.UNKNOWN_PRECISION)); + sqlTypeMapping.setScale(getInteger( + getAttribute(sqlType, "scale"), + SQLTypeMapping.UNKNOWN_SCALE)); + String notNull = getAttribute(sqlType, "not-null"); + if (StringHelper.isEmpty(notNull)) { + sqlTypeMapping.setNullable(null); + } else { + sqlTypeMapping.setNullable(notNull.equals("false")); + } + if (StringHelper.isEmpty(sqlTypeMapping.getHibernateType())) { + throw new MappingException( + "No hibernate-type specified for " + + sqlType.getAttribute("jdbc-type") + + " at " + + sqlType.getTagName()); + } + repository.addTypeMapping(sqlTypeMapping); + } + + private static String getHibernateType(Element element) { + String attributeValue = getAttribute(element, "hibernate-type"); + if(StringHelper.isEmpty(attributeValue)) { + ArrayList hibernateTypes = getChildElements(element, "hibernate-type"); + if ( !hibernateTypes.isEmpty() ) { + Element hibernateType = hibernateTypes.get(0); + if (hibernateType.hasAttribute("name")) { + return hibernateType.getAttribute("name"); + } + } else { + return null; + } + } + return attributeValue; + } + + private static int getInteger(String string, int defaultValue) { + if(string==null) { + return defaultValue; + } + else { + try { + return Integer.parseInt(string); + } catch (NumberFormatException e) { + throw new RuntimeException(e); + } + } + } + + private static ArrayList getChildElements(Element parent, String tagName) { + ArrayList result = new ArrayList(); + NodeList nodeList = parent.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + if (node instanceof Element) { + if (tagName.equals(((Element)node).getTagName())) { + result.add((Element)node); + } + } + } + return result; + } + + private static String getAttribute(Element element, String attributeName) { + String result = null; + if (element.hasAttribute(attributeName)) { + result = element.getAttribute(attributeName); + } + return result; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/OverrideRepository.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/OverrideRepository.java new file mode 100644 index 000000000000..86421cb14bf6 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/OverrideRepository.java @@ -0,0 +1,814 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.strategy; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.commons.collections4.MapIterator; +import org.apache.commons.collections4.MultiValuedMap; +import org.hibernate.MappingException; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.MetaAttribute; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.AssociationInfo; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.RevengStrategy.SchemaSelection; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.core.strategy.MetaAttributeHelper.SimpleMetaAttribute; +import org.hibernate.tool.reveng.internal.util.JdbcToHibernateTypeHelper; +import org.hibernate.tool.reveng.internal.util.TableNameQualifier; +import org.jboss.logging.Logger; +import org.w3c.dom.Document; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +public class OverrideRepository { + + final private static Logger log = Logger.getLogger( OverrideRepository.class ); + + final private Map> typeMappings; // from sqltypes to list of SQLTypeMapping + + final private List tableFilters; + + final private Map> foreignKeys; // key: TableIdentifier element: List of foreignkeys that references the Table + + final private Map typeForColumn; + + final private Map propertyNameForColumn; + + final private Map identifierStrategyForTable; + + final private Map identifierPropertiesForTable; + + final private Map> primaryKeyColumnsForTable; + + final private Set excludedColumns; + + final private TableToClassName tableToClassName; + + final private List schemaSelections; + + final private Map propertyNameForPrimaryKey; + + final private Map compositeIdNameForTable; + + final private Map foreignKeyToOneName; + + final private Map foreignKeyToInverseName; + + final private Map foreignKeyInverseExclude; + + final private Map foreignKeyToOneExclude; + + final private Map foreignKeyToEntityInfo; + final private Map foreignKeyToInverseEntityInfo; + + final private Map> tableMetaAttributes; // TI -> MultiMap of SimpleMetaAttributes + + final private Map> columnMetaAttributes; + + //private String defaultCatalog; + //private String defaultSchema; + + public OverrideRepository() { + //this.defaultCatalog = null; + //this.defaultSchema = null; + typeMappings = new HashMap>(); + tableFilters = new ArrayList(); + foreignKeys = new HashMap>(); + typeForColumn = new HashMap(); + propertyNameForColumn = new HashMap(); + identifierStrategyForTable = new HashMap(); + identifierPropertiesForTable = new HashMap(); + primaryKeyColumnsForTable = new HashMap>(); + propertyNameForPrimaryKey = new HashMap(); + tableToClassName = new TableToClassName(); + excludedColumns = new HashSet(); + schemaSelections = new ArrayList(); + compositeIdNameForTable = new HashMap(); + foreignKeyToOneName = new HashMap(); + foreignKeyToInverseName = new HashMap(); + foreignKeyInverseExclude = new HashMap(); + foreignKeyToOneExclude = new HashMap(); + tableMetaAttributes = new HashMap>(); + columnMetaAttributes = new HashMap>(); + foreignKeyToEntityInfo = new HashMap(); + foreignKeyToInverseEntityInfo = new HashMap(); + } + + public void addFile(File xmlFile) { + log.info( "Override file: " + xmlFile.getPath() ); + try { + addInputStream( xmlFile ); + } + catch ( Exception e ) { + log.error( "Could not configure overrides from file: " + xmlFile.getPath(), e ); + throw new MappingException( "Could not configure overrides from file: " + xmlFile.getPath(), e ); + } + + } + + /** + * Read override from an application resource trying different classloaders. + * This method will try to load the resource first from the thread context + * classloader and then from the classloader that loaded Hibernate. + */ + public OverrideRepository addResource(String path) throws MappingException { + log.info( "Mapping resource: " + path ); + InputStream rsrc = Thread.currentThread().getContextClassLoader().getResourceAsStream( path ); + if ( rsrc == null ) rsrc = OverrideRepository.class.getClassLoader().getResourceAsStream( path ); + if ( rsrc == null ) throw new MappingException( "Resource: " + path + " not found" ); + try { + return addInputStream( rsrc ); + } + catch ( MappingException me ) { + throw new MappingException( "Error reading resource: " + path, me ); + } + } + + public OverrideRepository addInputStream(InputStream xmlInputStream) throws MappingException { + try { + final List errors = new ArrayList(); + ErrorHandler errorHandler = new ErrorHandler() { + @Override + public void warning(SAXParseException exception) throws SAXException { + log.warn("warning while parsing xml", exception); + } + @Override + public void error(SAXParseException exception) throws SAXException { + errors.add(exception); + } + @Override + public void fatalError(SAXParseException exception) throws SAXException { + error(exception); + } + }; + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + db.setErrorHandler(errorHandler); + Document document = db.parse(xmlInputStream); + if ( !errors.isEmpty()) throw new MappingException( "invalid override definition", ( Throwable ) errors.get( 0 ) ); + add( document ); + return this; + } + catch ( MappingException me ) { + throw me; + } + catch ( Exception e ) { + log.error( "Could not configure overrides from input stream", e ); + throw new MappingException( e ); + } + } + + public void addInputStream(File file) throws MappingException { + InputStream xmlInputStream = null; + try { + xmlInputStream = new FileInputStream( file ); + addInputStream( xmlInputStream ); + } + catch ( Exception e ) { + log.error( "Could not configure overrides from input stream", e ); + throw new MappingException( e ); + } + finally { + try { + if (xmlInputStream != null) { + xmlInputStream.close(); + } + } + catch ( IOException ioe ) { + log.error( "could not close input stream", ioe ); + } + } + } + + private void add(Document doc) { + OverrideBinder.bindRoot(this, doc); + } + + private String getPreferredHibernateType(int sqlType, int length, int precision, int scale, boolean nullable) { + List l = typeMappings.get(new TypeMappingKey(sqlType,length) ); + + if(l == null) { // if no precise length match found, then try to find matching unknown length matches + l = typeMappings.get(new TypeMappingKey(sqlType,SQLTypeMapping.UNKNOWN_LENGTH) ); + } + return scanForMatch( sqlType, length, precision, scale, nullable, l ); + } + + private String scanForMatch(int sqlType, int length, int precision, int scale, boolean nullable, List l) { + if(l!=null) { + for ( SQLTypeMapping element : l ) { + if ( element.getJDBCType() != sqlType ) return null; + if ( element.match( sqlType, length, precision, scale, nullable ) ) { + return element.getHibernateType(); + } + } + } + return null; + } + + public void addTypeMapping(SQLTypeMapping sqltype) { + TypeMappingKey key = new TypeMappingKey(sqltype); + List list = typeMappings.computeIfAbsent( key, k -> new ArrayList<>() ); + list.add(sqltype); + } + + static class TypeMappingKey { + + int type; + int length; + + TypeMappingKey(SQLTypeMapping mpa) { + type = mpa.getJDBCType(); + length = mpa.getLength(); + } + + public TypeMappingKey(int sqlType, int length) { + this.type = sqlType; + this.length = length; + } + + public boolean equals(Object obj) { + if(obj==null) return false; + if(!(obj instanceof TypeMappingKey other)) return false; + + + return type==other.type && length==other.length; + } + + public int hashCode() { + return (type + length) % 17; + } + + public String toString() { + return this.getClass() + "(type:" + type + ", length:" + length + ")"; + } + } + + protected String getPackageName(TableIdentifier identifier) { + for ( TableFilter tf : tableFilters ) { + String value = tf.getPackage( identifier ); + if ( value != null ) { + return value; + } + } + return null; + } + + protected boolean excludeTable(TableIdentifier identifier) { + Iterator iterator = tableFilters.iterator(); + boolean hasInclude = false; + + while(iterator.hasNext() ) { + TableFilter tf = iterator.next(); + Boolean value = tf.exclude(identifier); + if(value!=null) { + return value; + } + if(!tf.getExclude() ) { + hasInclude = true; + } + } + + return hasInclude; + } + + public void addTableFilter(TableFilter filter) { + tableFilters.add(filter); + } + + public RevengStrategy getReverseEngineeringStrategy(RevengStrategy delegate) { + return new DelegatingStrategy(delegate) { + + public boolean excludeTable(TableIdentifier ti) { + return OverrideRepository.this.excludeTable(ti); + } + + public Map tableToMetaAttributes(TableIdentifier tableIdentifier) { + return OverrideRepository.this.tableToMetaAttributes(tableIdentifier); + } + + public Map columnToMetaAttributes(TableIdentifier tableIdentifier, String column) { + return OverrideRepository.this.columnToMetaAttributes(tableIdentifier, column); + } + + public boolean excludeColumn(TableIdentifier identifier, String columnName) { + return excludedColumns.contains(new TableColumnKey(identifier, columnName)); + } + + public String tableToCompositeIdName(TableIdentifier identifier) { + String result = compositeIdNameForTable.get(identifier); + if(result==null) { + return super.tableToCompositeIdName(identifier); + } else { + return result; + } + } + public List getSchemaSelections() { + if(schemaSelections.isEmpty()) { + return super.getSchemaSelections(); + } else { + return schemaSelections; + } + } + + public String columnToHibernateTypeName(TableIdentifier table, String columnName, int sqlType, int length, int precision, int scale, boolean nullable, boolean generatedIdentifier) { + String result = null; + String location = ""; + String info = " t:" + JdbcToHibernateTypeHelper.getJDBCTypeName( sqlType ) + " l:" + length + " p:" + precision + " s:" + scale + " n:" + nullable + " id:" + generatedIdentifier; + if(table!=null) { + location = TableNameQualifier.qualify(table.getCatalog(), table.getSchema(), table.getName() ) + "." + columnName; + } else { + + location += " Column: " + columnName + info; + } + if(table!=null && columnName!=null) { + result = typeForColumn.get(new TableColumnKey(table, columnName)); + if(result!=null) { + log.debug("explicit column mapping found for [" + location + "] to [" + result + "]"); + return result; + } + } + + result = OverrideRepository.this.getPreferredHibernateType(sqlType, length, precision, scale, nullable); + if(result==null) { + return super.columnToHibernateTypeName(table, columnName, sqlType, length, precision, scale, nullable, generatedIdentifier); + } + else { + log.debug(" found for [" + location + info + "] to [" + result + "]"); + return result; + } + } + + public String tableToClassName(TableIdentifier tableIdentifier) { + String className = tableToClassName.get(tableIdentifier); + + if(className!=null) { + if( className.contains( "." ) ) { + return className; + } else { + String packageName = getPackageName(tableIdentifier); + if(packageName==null) { + return className; + } else { + return StringHelper.qualify(packageName, className); + } + } + } + + String packageName = getPackageName(tableIdentifier); + if(packageName==null) { + return super.tableToClassName(tableIdentifier); + } + else { + String string = super.tableToClassName(tableIdentifier); + if(string==null) return null; + return StringHelper.qualify(packageName, StringHelper.unqualify(string)); + } + } + + public List getForeignKeys(TableIdentifier referencedTable) { + List list = foreignKeys.get(referencedTable); + if(list==null) { + return super.getForeignKeys(referencedTable); + } else { + return list; + } + } + + public String columnToPropertyName(TableIdentifier table, String column) { + String result = propertyNameForColumn.get(new TableColumnKey(table, column)); + if(result==null) { + return super.columnToPropertyName(table, column); + } else { + return result; + } + } + + public String tableToIdentifierPropertyName(TableIdentifier tableIdentifier) { + String result = propertyNameForPrimaryKey.get(tableIdentifier); + if(result==null) { + return super.tableToIdentifierPropertyName(tableIdentifier); + } else { + return result; + } + } + + public String getTableIdentifierStrategyName(TableIdentifier tableIdentifier) { + String result = identifierStrategyForTable.get(tableIdentifier); + if(result==null) { + return super.getTableIdentifierStrategyName( tableIdentifier ); + } else { + log.debug("tableIdentifierStrategy for " + tableIdentifier + " -> '" + result + "'"); + return result; + } + } + + public Properties getTableIdentifierProperties(TableIdentifier tableIdentifier) { + Properties result = identifierPropertiesForTable.get(tableIdentifier); + if(result==null) { + return super.getTableIdentifierProperties( tableIdentifier ); + } else { + return result; + } + } + + public List getPrimaryKeyColumnNames(TableIdentifier tableIdentifier) { + List result = primaryKeyColumnsForTable.get(tableIdentifier); + if(result==null) { + return super.getPrimaryKeyColumnNames(tableIdentifier); + } else { + return result; + } + } + + public String foreignKeyToEntityName(String keyname, TableIdentifier fromTable, List fromColumnNames, TableIdentifier referencedTable, List referencedColumnNames, boolean uniqueReference) { + String property = foreignKeyToOneName.get(keyname); + if(property==null) { + return super.foreignKeyToEntityName(keyname, fromTable, fromColumnNames, referencedTable, referencedColumnNames, uniqueReference); + } else { + return property; + } + } + + + public String foreignKeyToInverseEntityName(String keyname, + TableIdentifier fromTable, List fromColumnNames, + TableIdentifier referencedTable, + List referencedColumnNames, boolean uniqueReference) { + + String property = foreignKeyToInverseName.get(keyname); + if(property==null) { + return super.foreignKeyToInverseEntityName(keyname, fromTable, fromColumnNames, referencedTable, referencedColumnNames, uniqueReference); + } else { + return property; + } + } + + public String foreignKeyToCollectionName(String keyname, TableIdentifier fromTable, List fromColumns, TableIdentifier referencedTable, List referencedColumns, boolean uniqueReference) { + String property = foreignKeyToInverseName.get(keyname); + if(property==null) { + return super.foreignKeyToCollectionName(keyname, fromTable, fromColumns, referencedTable, referencedColumns, uniqueReference); + } else { + return property; + } + } + + public boolean excludeForeignKeyAsCollection( + String keyname, + TableIdentifier fromTable, + List fromColumns, + TableIdentifier referencedTable, + List referencedColumns) { + Boolean bool = foreignKeyInverseExclude.get(keyname); + if(bool!=null) { + return bool; + } else { + return super.excludeForeignKeyAsCollection( keyname, fromTable, fromColumns, + referencedTable, referencedColumns ); + } + } + + public boolean excludeForeignKeyAsManytoOne( + String keyname, + TableIdentifier fromTable, + List fromColumns, TableIdentifier + referencedTable, + List referencedColumns) { + Boolean bool = (Boolean) foreignKeyToOneExclude.get(keyname); + if(bool!=null) { + return bool; + } else { + return super.excludeForeignKeyAsManytoOne( keyname, fromTable, fromColumns, + referencedTable, referencedColumns ); + } + } + + + public AssociationInfo foreignKeyToInverseAssociationInfo(ForeignKey foreignKey) { + AssociationInfo fkei = foreignKeyToInverseEntityInfo.get(foreignKey.getName()); + if(fkei!=null) { + return fkei; + } else { + return super.foreignKeyToInverseAssociationInfo(foreignKey); + } + } + + public AssociationInfo foreignKeyToAssociationInfo(ForeignKey foreignKey) { + AssociationInfo fkei = foreignKeyToEntityInfo.get(foreignKey.getName()); + if(fkei!=null) { + return fkei; + } else { + return super.foreignKeyToAssociationInfo(foreignKey); + } + } + }; + } + + protected Map columnToMetaAttributes(TableIdentifier tableIdentifier, String column) { + MultiValuedMap specific = columnMetaAttributes.get( new TableColumnKey(tableIdentifier, column) ); + if(specific!=null && !specific.isEmpty()) { + return toMetaAttributes(specific); + } + + return null; + } + + // TODO: optimize + protected Map tableToMetaAttributes(TableIdentifier identifier) { + MultiValuedMap specific = tableMetaAttributes.get( identifier ); + if(specific!=null && !specific.isEmpty()) { + return toMetaAttributes(specific); + } + MultiValuedMap general = findGeneralAttributes( identifier ); + if(general!=null && !general.isEmpty()) { + return toMetaAttributes(general); + } + + return null; + + /* inheritance not defined yet + if(specific==null) { specific = Collections.EMPTY_MAP; } + if(general==null) { general = Collections.EMPTY_MAP; } + + MultiMap map = MetaAttributeBinder.mergeMetaMaps( specific, general ); + */ + /* + if(map!=null && !map.isEmpty()) { + return toMetaAttributes(null, map); + } else { + return null; + } + */ + } + + private MultiValuedMap findGeneralAttributes(TableIdentifier identifier) { + for ( TableFilter tf : tableFilters ) { + MultiValuedMap value = tf.getMetaAttributes( identifier ); + if ( value != null ) { + return value; + } + } + return null; + } + + private Map toMetaAttributes(MultiValuedMap mvm) { + Map result = new HashMap(); + for (MapIterator iter = mvm.mapIterator(); iter.hasNext();) { + String key = iter.next(); + Collection values = mvm.get(key); + result.put(key, MetaAttributeHelper.toRealMetaAttribute(key, values)); + } + return result; + } + + /** + * @deprecated Use {@link #getReverseEngineeringStrategy(RevengStrategy)} + * with {@code delegate=null} to explicitly ignore the delegate. + */ + @Deprecated + public RevengStrategy getReverseEngineeringStrategy() { + return getReverseEngineeringStrategy(null); + } + + public void addTable(Table table, String wantedClassName) { + for (ForeignKey fk : table.getForeignKeys().values()) { + TableIdentifier identifier = TableIdentifier.create(fk.getReferencedTable()); + List existing = foreignKeys.computeIfAbsent( identifier, k -> new ArrayList<>() ); + existing.add( fk ); + } + + if(StringHelper.isNotEmpty(wantedClassName)) { + TableIdentifier tableIdentifier = TableIdentifier.create(table); + String className = wantedClassName; + /* If wantedClassName specifies a package, it is given by +
config so do no more. */ + if(!wantedClassName.contains(".")) { + /* Now look for the package name specified by + config. */ + String packageName = getPackageName(tableIdentifier); + if (packageName != null && !packageName.isBlank()) { + className = packageName + "." + wantedClassName; + } + } + tableToClassName.put(tableIdentifier, className); + } + } + + static class TableColumnKey { + private final TableIdentifier query; + private final String name; + + TableColumnKey(TableIdentifier query, String name){ + this.query = query; + this.name = name; + } + + @Override + public int hashCode() { + final int prime = 29; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((query == null) ? 0 : query.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + TableColumnKey other = (TableColumnKey) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (query == null) { + return other.query == null; + } else return query.equals( other.query ); + } + + } + + public void setTypeNameForColumn(TableIdentifier identifier, String columnName, String type) { + if(StringHelper.isNotEmpty(type)) { + typeForColumn.put(new TableColumnKey(identifier, columnName), type); + } + } + + public void setExcludedColumn(TableIdentifier tableIdentifier, String columnName) { + excludedColumns.add(new TableColumnKey(tableIdentifier, columnName)); + } + + public void setPropertyNameForColumn(TableIdentifier identifier, String columnName, String property) { + if(StringHelper.isNotEmpty(property)) { + propertyNameForColumn.put(new TableColumnKey(identifier, columnName), property); + } + } + + public void addTableIdentifierStrategy(Table table, String identifierClass, Properties params) { + if(identifierClass!=null) { + final TableIdentifier tid = TableIdentifier.create(table); + identifierStrategyForTable.put(tid, identifierClass); + identifierPropertiesForTable.put(tid, params); + } + } + + public void addPrimaryKeyNamesForTable(Table table, List boundColumnNames, String propertyName, String compositeIdName) { + TableIdentifier tableIdentifier = TableIdentifier.create(table); + if(boundColumnNames!=null && !boundColumnNames.isEmpty()) { + primaryKeyColumnsForTable.put(tableIdentifier, boundColumnNames); + } + if(StringHelper.isNotEmpty(propertyName)) { + propertyNameForPrimaryKey.put(tableIdentifier, propertyName); + } + if(StringHelper.isNotEmpty(compositeIdName)) { + compositeIdNameForTable.put(tableIdentifier, compositeIdName); + } + } + + /*public String getCatalog(String string) { + return string==null?defaultCatalog:string; + }*/ + + /*public String getSchema(String string) { + return string==null?defaultSchema:string; + }*/ + + public void addSchemaSelection(SchemaSelection schemaSelection) { + schemaSelections.add(schemaSelection); + } + + /** + * Both sides of the FK are important, + * the owning side can generate a toOne (ManyToOne or OneToOne), we call this side foreignKeyToOne + * the inverse side can generate a OneToMany OR a OneToOne (in case we have a pure bidirectional OneToOne, we call this side foreignKeyToInverse + */ + public void addForeignKeyInfo(String constraintName, String toOneProperty, Boolean excludeToOne, String inverseProperty, Boolean excludeInverse, AssociationInfo associationInfo, AssociationInfo inverseAssociationInfo) { + if(StringHelper.isNotEmpty(toOneProperty)) { + foreignKeyToOneName.put(constraintName, toOneProperty); + } + if(StringHelper.isNotEmpty(inverseProperty)) { + foreignKeyToInverseName.put(constraintName, inverseProperty); + } + if(excludeInverse!=null) { + foreignKeyInverseExclude.put(constraintName, excludeInverse); + } + if(excludeToOne!=null) { + foreignKeyToOneExclude.put(constraintName, excludeToOne); + } + if(associationInfo!=null) { + foreignKeyToEntityInfo.put(constraintName, associationInfo); + } + if(inverseAssociationInfo!=null) { + foreignKeyToInverseEntityInfo.put(constraintName, inverseAssociationInfo); + } + + } + + public void addMetaAttributeInfo(Table table, MultiValuedMap map) { + if(map!=null && !map.isEmpty()) { + tableMetaAttributes.put(TableIdentifier.create(table), map); + } + + } + + public void addMetaAttributeInfo( + TableIdentifier tableIdentifier, + String name, + MultiValuedMap map) { + if(map!=null && !map.isEmpty()) { + columnMetaAttributes.put(new TableColumnKey( tableIdentifier, name ), map); + } + + } + + /*It is not possible to match a table on TableMapper alone because RootClassBinder.bind() + calls nullifyDefaultCatalogAndSchema(table) before doing this TableToClassName lookup. + So only use the table name for initial matching, and catalog or schema names when they + are not null. + */ + + private static class TableToClassName { + Map map = new HashMap(); + + private String get(TableIdentifier tableIdentifier) { + TableMapper mapper = map.get(tableIdentifier.getName()); + if (mapper != null) { + if (mapper.catalog == null || tableIdentifier.getCatalog() == null || + mapper.catalog.equals(tableIdentifier.getCatalog())){ + if (mapper.schema == null || tableIdentifier.getSchema() == null || + mapper.schema.equals(tableIdentifier.getSchema())){ + if ( mapper.packageName.isEmpty() ) { + return mapper.className; + } else { + return mapper.packageName + "." + mapper.className; + } + } + } + } + return null; + } + + private void put(TableIdentifier tableIdentifier, String wantedClassName) { + TableMapper tableMapper = new TableMapper( + tableIdentifier.getCatalog(), + tableIdentifier.getSchema(), + wantedClassName ); + map.put(tableIdentifier.getName(), tableMapper); + } + } + + private static class TableMapper { + String catalog; + String schema; + String className; + String packageName; + + private TableMapper(String catalog, String schema, String wantedClassName) { + this.catalog = catalog; + this.schema = schema; + if (wantedClassName.contains(".")) { + int nameStartPos = wantedClassName.lastIndexOf("."); + this.className = wantedClassName.substring(nameStartPos+1); + this.packageName = wantedClassName.substring(0, nameStartPos); + } else { + this.className = wantedClassName; + this.packageName = ""; + } + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/SQLTypeMapping.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/SQLTypeMapping.java new file mode 100644 index 000000000000..14507a5d3e78 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/SQLTypeMapping.java @@ -0,0 +1,167 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.strategy; + +public class SQLTypeMapping implements Comparable { + + //static public final int UNKNOWN_TYPE = Integer.MAX_VALUE; + public static final int UNKNOWN_LENGTH = Integer.MAX_VALUE; + public static final int UNKNOWN_PRECISION = Integer.MAX_VALUE; + public static final int UNKNOWN_SCALE = Integer.MAX_VALUE; + public static final Boolean UNKNOWN_NULLABLE = null; + + private final int jdbcType; + private int length = UNKNOWN_LENGTH; + private int precision = UNKNOWN_PRECISION; + private int scale = UNKNOWN_SCALE; + private Boolean nullable; + + private String hibernateType; + + public SQLTypeMapping(int jdbcType) { + this.jdbcType = jdbcType; + } + + /*public void setJDBCType(int jdbcType) { + this.jdbcType = jdbcType; + }*/ + + public SQLTypeMapping(int sqlType, int length, int precision, int scale, Boolean nullable) { + this.jdbcType = sqlType; + this.length = length; + this.precision = precision; + this.scale = scale; + this.nullable = nullable; + } + + public void setLength(int length) { + this.length = length; + } + + public void setHibernateType(String hibernateType) { + this.hibernateType = hibernateType; + } + + public void setNullable(Boolean nullable) { + this.nullable = nullable; + } + + public Boolean getNullable() { + return nullable; + } + + public int getJDBCType() { + return jdbcType; + } + + public String getHibernateType() { + return hibernateType; + } + + public int getLength() { + return length; + } + + public String toString() { + return getJDBCType() + " l:" + getLength() + " p:" + getPrecision() + " s:" + getScale() + " n:" + getNullable() + " ht:" + getHibernateType(); + } + + public int getPrecision() { + return precision; + } + + public void setPrecision(int precision) { + this.precision = precision; + } + + public int getScale() { + return scale; + } + + public void setScale(int scale) { + this.scale = scale; + } + + public boolean match(int matchjdbctype, int matchlength, int matchprecision, int matchscale, boolean matchnullable) { + if(matchjdbctype==this.jdbcType) {// this always need to be exact + if(matchlength==this.length || this.length == UNKNOWN_LENGTH) { + if(matchprecision==this.precision || this.precision == UNKNOWN_PRECISION) { + if(matchscale==this.scale || this.scale == UNKNOWN_SCALE ) { + return this.nullable == UNKNOWN_NULLABLE || nullable.equals( matchnullable ); + } + } + } + } + return false; + } + + public int compareTo(SQLTypeMapping other) { + if(this.jdbcType==other.jdbcType) { + if(this.length==other.length) { + if(this.precision==other.precision) { + if(this.scale==other.scale) { + return compare(this.nullable, other.nullable); + } else { + return compare(this.scale, other.scale); + } + } + else { + return compare(this.precision,other.precision); + } + } + else { + return compare(this.length,other.length); + } + } + else { + return compare(this.jdbcType,other.jdbcType); + } + } + + private int compare(int value, int other) { + return Integer.compare( value, other ); + } + + // complete ordering of the tri-state: false, true, UNKNOWN_NULLABLE + private int compare(Boolean value, Boolean other) { + if(value==other) return 0; + if(value==UNKNOWN_NULLABLE) return 1; + if(other==UNKNOWN_NULLABLE) return -1; + if(value.equals(other)) return 0; + if(value.equals(Boolean.TRUE)) { + return 1; + } else { + return -1; + } + } + + public boolean equals(Object obj) { + if (getClass().isAssignableFrom(obj.getClass())) { + SQLTypeMapping other = getClass().cast(obj); + return compareTo(other)==0; + } else { + return false; + } + } + + public int hashCode() { + return (jdbcType + length + precision + scale + (nullable==UNKNOWN_NULLABLE?1:nullable.hashCode())) % 17; + } + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/TableFilter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/TableFilter.java new file mode 100644 index 000000000000..03271c772d53 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/TableFilter.java @@ -0,0 +1,176 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.strategy; + +import org.apache.commons.collections4.MultiValuedMap; +import org.hibernate.tool.reveng.api.core.TableIdentifier; +import org.hibernate.tool.reveng.internal.core.strategy.MetaAttributeHelper.SimpleMetaAttribute; + + +/** + * + * A tablefilter that can tell if a TableIdentifier is included or excluded. + * Note that all matching is case sensitive since many db's are. + * + * @author max + * + */ +public class TableFilter { + + // TODO: very basic substring matching. Possibly include regex functionallity ? (jdk 1.4 dep) + public static class Matcher { + + private static final int EQUALS = 1; + private static final int ENDSWITH = 2; + private static final int STARTSWITH = 3; + private static final int SUBSTRING = 4; + private static final int ANY = 5; + + final int mode; + final String value; + final String matchValue; + + Matcher(String match) { + matchValue = match; + if(".*".equals(match) ) { + mode = ANY; + value = null; + } + else if(match.length()>4 && match.startsWith(".*") && match.endsWith(".*") ) { + mode = SUBSTRING; + value = match.substring(2, match.length()-2); + } + else if(match.endsWith(".*") ) { + mode = STARTSWITH; + value = match.substring(0, match.length()-2); + } + else if (match.startsWith(".*") ){ + mode = ENDSWITH; + value = match.substring(2); + } + else { + mode = EQUALS; + value = match; + } + } + + boolean match(String matchEnum) { + switch (mode) { + case ANY: return true; + case EQUALS: return this.value.equals(matchEnum); + case ENDSWITH: return matchEnum.endsWith(this.value); + case STARTSWITH: return matchEnum.startsWith(this.value); + case SUBSTRING: return matchEnum.indexOf(this.value)>=0; + default: + throw new IllegalStateException(); + } + } + + public String toString() { + return matchValue; + } + } + + private Boolean exclude; + private String packageName; + + private Matcher catalogMatcher; + private Matcher schemaMatcher; + private Matcher nameMatcher; + private MultiValuedMap metaAttributes; + + + + public TableFilter() { + setMatchCatalog(".*"); + setMatchSchema(".*"); + setMatchName(".*"); + setExclude(null); + } + + public void setMatchCatalog(String matchCatalog) { + this.catalogMatcher = new Matcher(matchCatalog); + } + + public void setMatchSchema(String matchSchema) { + this.schemaMatcher = new Matcher(matchSchema); + } + + public void setMatchName(String matchName) { + this.nameMatcher = new Matcher(matchName); + } + + /** + * + * @return null if filter does not affect this identifier, true/false if it does. + */ + public Boolean exclude(TableIdentifier identifier) { + return isRelevantFor(identifier) ? exclude : null; + } + + public void setExclude(Boolean bool) { + exclude = bool; + } + + public String getPackage(TableIdentifier identifier) { + return isRelevantFor(identifier) ? packageName : null; + } + + private boolean isRelevantFor(TableIdentifier identifier) { + if(catalogMatcher.match(identifier.getCatalog() ) ) { + if(schemaMatcher.match(identifier.getSchema() ) ) { + if(nameMatcher.match(identifier.getName() ) ) { + return true; + } + } + } + return false; + } + + public void setPackage(String string) { + packageName = string; + } + + public String toString() { + return catalogMatcher + " " + schemaMatcher + " " + nameMatcher + " " + exclude; + } + + public String getMatchCatalog() { + return catalogMatcher.matchValue; + } + + public String getMatchSchema() { + return schemaMatcher.matchValue; + } + + public String getMatchName() { + return nameMatcher.matchValue; + } + + public Boolean getExclude() { + return exclude; + } + + public MultiValuedMap getMetaAttributes(TableIdentifier identifier) { + return isRelevantFor(identifier) ? metaAttributes : null; + } + + public void setMetaAttributes(MultiValuedMap metaAttributes) { + this.metaAttributes = metaAttributes; + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/TableSelectorStrategy.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/TableSelectorStrategy.java new file mode 100644 index 000000000000..ebebc2c410e8 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/strategy/TableSelectorStrategy.java @@ -0,0 +1,45 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.strategy; + +import org.hibernate.tool.reveng.api.core.RevengStrategy; + +import java.util.ArrayList; +import java.util.List; + +public class TableSelectorStrategy extends DelegatingStrategy { + + List selections = new ArrayList(); + + public TableSelectorStrategy(RevengStrategy res) { + super(res); + } + + public List getSchemaSelections() { + return selections; + } + + + public void clearSchemaSelections() { + selections.clear(); + } + + public void addSchemaSelection(SchemaSelection selection) { + selections.add(selection); + } +} \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedBasicValue.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedBasicValue.java new file mode 100644 index 000000000000..0b865fe2af0e --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedBasicValue.java @@ -0,0 +1,57 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2024-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.util; + +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.mapping.BasicValue; +import org.hibernate.mapping.Table; + +import java.util.Properties; + +@SuppressWarnings("serial") +public class EnhancedBasicValue extends BasicValue implements EnhancedValue { + + private Properties idGenProps = new Properties(); + private String genStrategy = null; + + public EnhancedBasicValue(MetadataBuildingContext buildingContext, Table table) { + super(buildingContext, table); + } + + @Override + public void setIdentifierGeneratorProperties(Properties props) { + idGenProps = props; + + } + + @Override + public Properties getIdentifierGeneratorProperties() { + return idGenProps; + } + + @Override + public void setIdentifierGeneratorStrategy(String s) { + genStrategy = s; + } + + @Override + public String getIdentifierGeneratorStrategy() { + return genStrategy; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedComponent.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedComponent.java new file mode 100644 index 000000000000..539c0a0150da --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedComponent.java @@ -0,0 +1,65 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2024-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.util; + +import org.hibernate.MappingException; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.PersistentClass; + +import java.util.Properties; + +@SuppressWarnings("serial") +public class EnhancedComponent extends Component implements EnhancedValue { + + private Properties idGenProps = new Properties(); + private String genStrategy = null; + + public EnhancedComponent(MetadataBuildingContext metadata, PersistentClass owner) throws MappingException { + super(metadata, owner); + } + + @Override + public void setIdentifierGeneratorProperties(Properties props) { + idGenProps = props; + + } + + @Override + public Properties getIdentifierGeneratorProperties() { + return idGenProps; + } + + @Override + public void setIdentifierGeneratorStrategy(String s) { + genStrategy = s; + } + + @Override + public String getIdentifierGeneratorStrategy() { + return genStrategy; + } + + @Override + public Class getComponentClass() throws MappingException { + // we prevent ORM from trying to load a component class by name, + // since at the point when we are building these, a corresponding class is not yet created + // (so can't even think about it being compiled and able to load via any classloader) ... + return Object.class; + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedValue.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedValue.java new file mode 100644 index 000000000000..7e51d3dc9b8e --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/EnhancedValue.java @@ -0,0 +1,34 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2024-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.util; + +import org.hibernate.mapping.KeyValue; + +import java.util.Properties; + +public interface EnhancedValue extends KeyValue { + + void setIdentifierGeneratorProperties(Properties suggestedProperties); + + Properties getIdentifierGeneratorProperties(); + + void setIdentifierGeneratorStrategy(String s); + + String getIdentifierGeneratorStrategy(); + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/RevengUtils.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/RevengUtils.java new file mode 100644 index 000000000000..010c611b383f --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/core/util/RevengUtils.java @@ -0,0 +1,112 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.util; + +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.core.AssociationInfo; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.TableIdentifier; + +import java.util.List; + +public class RevengUtils { + + public static List getPrimaryKeyInfoInRevengStrategy( + RevengStrategy revengStrat, + Table table, + String defaultCatalog, + String defaultSchema) { + List result = null; + TableIdentifier tableIdentifier = TableIdentifier.create(table); + result = revengStrat.getPrimaryKeyColumnNames(tableIdentifier); + if (result == null) { + String catalog = getCatalogForModel(table.getCatalog(), defaultCatalog); + String schema = getSchemaForModel(table.getSchema(), defaultSchema); + tableIdentifier = TableIdentifier.create(catalog, schema, table.getName()); + result = revengStrat.getPrimaryKeyColumnNames(tableIdentifier); + } + return result; + } + + public static String getTableIdentifierStrategyNameInRevengStrategy( + RevengStrategy revengStrat, + TableIdentifier tableIdentifier, + String defaultCatalog, + String defaultSchema) { + String result = null; + result = revengStrat.getTableIdentifierStrategyName(tableIdentifier); + if (result == null) { + String catalog = getCatalogForModel(tableIdentifier.getCatalog(), defaultCatalog); + String schema = getSchemaForModel(tableIdentifier.getSchema(), defaultSchema); + tableIdentifier = TableIdentifier.create(catalog, schema, tableIdentifier.getName()); + result = revengStrat.getTableIdentifierStrategyName(tableIdentifier); + } + return result; + } + + public static TableIdentifier createTableIdentifier( + Table table, + String defaultCatalog, + String defaultSchema) { + String tableName = table.getName(); + String tableCatalog = getCatalogForModel(table.getCatalog(), defaultCatalog); + String tableSchema = getSchemaForModel(table.getSchema(), defaultSchema); + return TableIdentifier.create(tableCatalog, tableSchema, tableName); + } + + public static AssociationInfo createAssociationInfo( + String cascade, + String fetch, + Boolean insert, + Boolean update) { + return new AssociationInfo() { + @Override + public String getCascade() { + return cascade; + } + @Override + public String getFetch() { + return fetch; + } + @Override + public Boolean getUpdate() { + return update; + } + @Override + public Boolean getInsert() { + return insert; + } + + }; + } + + /** If catalog is equal to defaultCatalog then we return null so it will be null in the generated code. */ + private static String getCatalogForModel(String catalog, String defaultCatalog) { + if(catalog==null) return null; + if(catalog.equals(defaultCatalog)) return null; + return catalog; + } + + /** If catalog is equal to defaultSchema then we return null so it will be null in the generated code. */ + private static String getSchemaForModel(String schema, String defaultSchema) { + if(schema==null) return null; + if(schema.equals(defaultSchema)) return null; + return schema; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/cfg/CfgExporter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/cfg/CfgExporter.java new file mode 100644 index 000000000000..a38bb5f336c6 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/cfg/CfgExporter.java @@ -0,0 +1,203 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.cfg; + +import org.hibernate.cfg.Environment; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.RootClass; +import org.hibernate.mapping.Subclass; +import org.hibernate.tool.reveng.internal.export.common.AbstractExporter; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.TreeMap; + +/** + * @author max + * + */ +public class CfgExporter extends AbstractExporter { + + private Writer output; + private Properties customProperties = new Properties(); + + public Properties getCustomProperties() { + return customProperties; + } + + public void setCustomProperties(Properties customProperties) { + this.customProperties = customProperties; + } + + public Writer getOutput() { + return output; + } + + public void setOutput(Writer output) { + this.output = output; + } + + /* (non-Javadoc) + * @see org.hibernate.tool.hbm2x.Exporter#finish() + */ + public void doStart() { + PrintWriter pw = null; + File file = null; + try { + if(output==null) { + file = new File(getOutputDirectory(), "hibernate.cfg.xml"); + getTemplateHelper().ensureExistence(file); + pw = new PrintWriter(new FileWriter(file) ); + getArtifactCollector().addFile(file, "cfg.xml"); + } + else { + pw = new PrintWriter(output); + } + + + pw.println("\n" + + "\r\n" + + ""); + + boolean ejb3 = Boolean.valueOf((String)getProperties().get("ejb3")).booleanValue(); + + Map props = new TreeMap(); + if (getProperties() != null) { + props.putAll(getProperties()); + } + if(customProperties!=null) { + props.putAll(customProperties); + } + + String sfname = (String) props.get(Environment.SESSION_FACTORY_NAME); + pw.println(" "); + + Map ignoredProperties = new HashMap(); + ignoredProperties.put(Environment.SESSION_FACTORY_NAME, null); + ignoredProperties.put(Environment.HBM2DDL_AUTO, "false" ); + ignoredProperties.put("hibernate.temp.use_jdbc_metadata_defaults", null ); + ignoredProperties.put(Environment.TRANSACTION_COORDINATOR_STRATEGY, "org.hibernate.console.FakeTransactionManagerLookup"); + + Set> set = props.entrySet(); + Iterator> iterator = set.iterator(); + while (iterator.hasNext() ) { + Entry element = iterator.next(); + String key = (String) element.getKey(); + if(ignoredProperties.containsKey( key )) { + Object ignoredValue = ignoredProperties.get( key ); + if(ignoredValue == null || element.getValue().equals(ignoredValue)) { + continue; + } + } + if(key.startsWith("hibernate.") ) { // if not starting with hibernate. not relevant for cfg.xml + pw.println(" " + forXML(element.getValue().toString()) + ""); + } + } + + if(getMetadata()!=null) { + Iterator classMappings = getMetadata().getEntityBindings().iterator(); + while (classMappings.hasNext() ) { + PersistentClass element = classMappings.next(); + if(element instanceof RootClass) { + dump(pw, ejb3, element); + } + } + } + pw.println(" \r\n" + + ""); + + } + + catch (IOException e) { + throw new RuntimeException("Problems while creating hibernate.cfg.xml", e); + } + finally { + if(pw!=null) { + pw.flush(); + pw.close(); + } + } + + } + + /** + * @param pw + * @param element + */ + private void dump(PrintWriter pw, boolean useClass, PersistentClass element) { + if(useClass) { + pw.println(""); + } else { + pw.println(""); + } + + Iterator directSubclasses = element.getDirectSubclasses().iterator(); + while (directSubclasses.hasNext() ) { + PersistentClass subclass = (PersistentClass)directSubclasses.next(); + dump(pw, useClass, subclass); + } + + } + + /** + * @param element + * @return + */ + private String getMappingFileResource(PersistentClass element) { + + return element.getClassName().replace('.', '/') + ".hbm.xml"; + } + + public String getName() { + return "cfg2cfgxml"; + } + + /** + * + * @param text + * @return String with escaped [<,>] special characters. + */ + public static String forXML(String text) { + if (text == null) return null; + final StringBuilder result = new StringBuilder(); + char[] chars = text.toCharArray(); + for (int i = 0; i < chars.length; i++){ + char character = chars[i]; + if (character == '<') { + result.append("<"); + } else if (character == '>'){ + result.append(">"); + } else { + result.append(character); + } + } + return result.toString(); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/AbstractExporter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/AbstractExporter.java new file mode 100644 index 000000000000..63a6409fe208 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/AbstractExporter.java @@ -0,0 +1,244 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.common; + +import org.hibernate.boot.Metadata; +import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.tool.reveng.api.export.ArtifactCollector; +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterConstants; +import org.hibernate.tool.reveng.api.metadata.MetadataDescriptor; +import org.hibernate.tool.reveng.internal.export.hbm.Cfg2HbmTool; +import org.hibernate.tool.reveng.internal.export.java.Cfg2JavaTool; +import org.jboss.logging.Logger; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Properties; + +/** + * Base exporter for the template and direct output generation. + * Sets up the template environment + * + * @author max and david + * @author koen + */ +public abstract class AbstractExporter implements Exporter, ExporterConstants { + + protected Logger log = Logger.getLogger(this.getClass()); + + private TemplateHelper vh; + private final Properties properties = new Properties(); + private Metadata metadata = null; + + private Iterator> iterator; + + private final Cfg2HbmTool c2h; + private final Cfg2JavaTool c2j; + + public AbstractExporter() { + c2h = new Cfg2HbmTool(); + c2j = new Cfg2JavaTool(); + properties.put(ARTIFACT_COLLECTOR, new DefaultArtifactCollector()); + properties.put(TEMPLATE_PATH, new String[0]); + } + + protected MetadataDescriptor getMetadataDescriptor() { + return (MetadataDescriptor)getProperties().get(METADATA_DESCRIPTOR); + } + + public Metadata getMetadata() { + if (metadata == null) { + metadata = buildMetadata(); + } + return metadata; + } + + protected File getOutputDirectory() { + return (File)getProperties().get(DESTINATION_FOLDER); + } + + public Properties getProperties() { + return properties; + } + + public ArtifactCollector getArtifactCollector() { + return (ArtifactCollector)getProperties().get(ARTIFACT_COLLECTOR); + } + + public String getName() { + return this.getClass().getName(); + } + + public Cfg2HbmTool getCfg2HbmTool() { + return c2h; + } + + public Cfg2JavaTool getCfg2JavaTool() { + return c2j; + } + + public void start() { + setTemplateHelper( new TemplateHelper() ); + setupTemplates(); + setupContext(); + doStart(); + cleanUpContext(); + setTemplateHelper(null); + getArtifactCollector().formatFiles(); + } + + abstract protected void doStart(); + + protected void cleanUpContext() { + if(getProperties()!=null) { + iterator = getProperties().entrySet().iterator(); + while ( iterator.hasNext() ) { + Entry element = iterator.next(); + Object value = transformValue(element.getValue()); + String key = element.getKey().toString(); + if(key.startsWith(ExporterSettings.PREFIX_KEY)) { + getTemplateHelper().removeFromContext(key.substring(ExporterSettings.PREFIX_KEY.length())); + } + getTemplateHelper().removeFromContext(key); + } + } + if(getOutputDirectory()!=null) { + getTemplateHelper().removeFromContext("outputdir"); + } + String[] templatePath = (String[])getProperties().get(TEMPLATE_PATH); + if(templatePath!=null) { + getTemplateHelper().removeFromContext("template_path"); + } + getTemplateHelper().removeFromContext("exporter"); + getTemplateHelper().removeFromContext("artifacts"); + if(getMetadata() != null) { + getTemplateHelper().removeFromContext("md"); + getTemplateHelper().removeFromContext("props"); + getTemplateHelper().removeFromContext("tables"); + } + getTemplateHelper().removeFromContext("c2h"); + getTemplateHelper().removeFromContext("c2j"); + } + + protected void setupContext() { + getTemplateHelper().setupContext(); + getTemplateHelper().putInContext("exporter", this); + getTemplateHelper().putInContext("c2h", getCfg2HbmTool()); + getTemplateHelper().putInContext("c2j", getCfg2JavaTool()); + if(getOutputDirectory()!=null) { + getTemplateHelper().putInContext("outputdir", getOutputDirectory()); + } + String[] templatePath = (String[])getProperties().get(TEMPLATE_PATH); + if(templatePath!=null) { + getTemplateHelper().putInContext("template_path", templatePath); + } + if(getProperties()!=null) { + iterator = getProperties().entrySet().iterator(); + while ( iterator.hasNext() ) { + Entry element = iterator.next(); + String key = element.getKey().toString(); + Object value = transformValue(element.getValue()); + getTemplateHelper().putInContext(key, value); + if(key.startsWith(ExporterSettings.PREFIX_KEY)) { + getTemplateHelper().putInContext(key.substring(ExporterSettings.PREFIX_KEY.length()), value); + if(key.endsWith(".toolclass")) { + try { + Class toolClass = ReflectHelper.classForName(value.toString(), this.getClass()); + Constructor toolClassConstructor = toolClass.getConstructor(new Class[] {}); + Object object = toolClassConstructor.newInstance(); + getTemplateHelper().putInContext(key.substring(ExporterSettings.PREFIX_KEY.length(),key.length()-".toolclass".length()), object); + } + catch (Exception e) { + throw new RuntimeException("Exception when instantiating tool " + element.getKey() + " with " + value,e); + } + } + } + } + } + getTemplateHelper().putInContext("artifacts", getArtifactCollector()); + if(getMetadata() != null) { + getTemplateHelper().putInContext("md", metadata); + getTemplateHelper().putInContext("props", getProperties()); + getTemplateHelper().putInContext("tables", metadata.collectTableMappings()); + } + } + + protected void setupTemplates() { + String[] templatePath = (String[])getProperties().get(TEMPLATE_PATH); + if(log.isDebugEnabled()) { + log.debug(getClass().getName() + " outputdir:" + getOutputDirectory() + " path: " + toString(templatePath) ); + } + getTemplateHelper().init(getOutputDirectory(), templatePath); + } + + protected void setTemplateHelper(TemplateHelper vh) { + this.vh = vh; + } + + protected TemplateHelper getTemplateHelper() { + return vh; + } + + protected File getFileForClassName(File baseDir, String className, String extension) { + String filename = StringHelper.unqualify(className) + extension; + String packagename = StringHelper.qualifier(className); + return new File(getDirForPackage(baseDir, packagename), filename); + } + + protected Metadata buildMetadata() { + return getMetadataDescriptor().createMetadata(); + } + + private File getDirForPackage(File baseDir, String packageName) { + String p = packageName == null ? "" : packageName; + return new File( baseDir, p.replace('.', File.separatorChar) ); + } + + private String toString(Object[] a) { + if (a == null) + return "null"; + if (a.length == 0) + return "[]"; + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < a.length; i++) { + if (i == 0) + buf.append('['); + else + buf.append(", "); + + buf.append(String.valueOf(a[i])); + } + buf.append("]"); + return buf.toString(); + } + + private Object transformValue(Object value) { + if("true".equals(value)) { + return Boolean.TRUE; + } + if("false".equals(value)) { + return Boolean.FALSE; + } + return value; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/ConfigurationNavigator.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/ConfigurationNavigator.java new file mode 100644 index 000000000000..94e701ee46c3 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/ConfigurationNavigator.java @@ -0,0 +1,87 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.common; + +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.tool.reveng.internal.export.java.Cfg2JavaTool; +import org.hibernate.tool.reveng.internal.export.java.ComponentPOJOClass; +import org.hibernate.tool.reveng.internal.export.java.POJOClass; +import org.jboss.logging.Logger; + +import java.util.Iterator; +import java.util.Map; + +/** + * @author max and david + */ +public class ConfigurationNavigator { + + private static final Logger log = Logger.getLogger(ConfigurationNavigator.class); + + /** + * @param clazz + */ + public static void collectComponents(Map components, PersistentClass clazz) { + Iterator iter = new Cfg2JavaTool().getPOJOClass(clazz).getAllPropertiesIterator(); + collectComponents( components, iter ); + } + + public static void collectComponents(Map components, POJOClass clazz) { + Iterator iter = clazz.getAllPropertiesIterator(); + collectComponents( components, iter ); + } + + private static void collectComponents(Map components, Iterator iter) { + while(iter.hasNext()) { + Property property = iter.next(); + if (!"embedded".equals(property.getPropertyAccessorName()) && // HBX-267, embedded property for should not be generated as component. + property.getValue() instanceof Component) { + Component comp = (Component) property.getValue(); + addComponent( components, comp ); + } + else if (property.getValue() instanceof Collection) { + // compisite-element in collection + Collection collection = (Collection) property.getValue(); + if ( collection.getElement() instanceof Component) { + Component comp = (Component) collection.getElement(); + addComponent(components, comp); + } + } + } + } + + private static void addComponent(Map components, Component comp) { + if(!comp.isDynamic()) { + Component existing = (Component) components.put( + comp.getComponentClassName(), + comp); + if(existing!=null) { + log.warn("Component " + existing.getComponentClassName() + " found more than once! Will only generate the last found."); + } + } else { + log.debug("dynamic-component found. Ignoring it as a component, but will collect any embedded components."); + } + collectComponents( + components, + new ComponentPOJOClass(comp, new Cfg2JavaTool()).getAllPropertiesIterator()); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/DefaultArtifactCollector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/DefaultArtifactCollector.java new file mode 100644 index 000000000000..09c787cae6c4 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/DefaultArtifactCollector.java @@ -0,0 +1,114 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.common; + +import org.hibernate.tool.reveng.api.export.ArtifactCollector; +import org.hibernate.tool.reveng.api.xml.XMLPrettyPrinter; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Callback class that all exporters are given to allow better feedback and + * processing of the output afterwards. + * + * @author Max Rydahl Andersen + * + */ +public class DefaultArtifactCollector implements ArtifactCollector { + + final protected Map> files = new HashMap>(); + + /* (non-Javadoc) + * @see org.hibernate.tool.internal.export.ArtifactCollector#addFile(java.io.File, java.lang.String) + */ + @Override + public void addFile(File file, String type) { + List existing = files.get(type); + if (existing == null) { + existing = new ArrayList(); + files.put(type, existing); + } + existing.add(file); + } + + /* (non-Javadoc) + * @see org.hibernate.tool.internal.export.ArtifactCollector#getFileCount(java.lang.String) + */ + @Override + public int getFileCount(String type) { + List existing = files.get(type); + + return (existing == null) ? 0 : existing.size(); + } + + /* (non-Javadoc) + * @see org.hibernate.tool.internal.export.ArtifactCollector#getFiles(java.lang.String) + */ + @Override + public File[] getFiles(String type) { + List existing = files.get(type); + + if (existing == null) { + return new File[0]; + } else { + return (File[]) existing.toArray(new File[existing.size()]); + } + } + + /* (non-Javadoc) + * @see org.hibernate.tool.internal.export.ArtifactCollector#getFileTypes() + */ + @Override + public Set getFileTypes() { + return files.keySet(); + } + + /* (non-Javadoc) + * @see org.hibernate.tool.internal.export.ArtifactCollector#formatFiles() + */ + @Override + public void formatFiles() { + + formatXml("xml"); + formatXml("hbm.xml"); + formatXml("cfg.xml"); + + } + + private void formatXml(String type) { + List list = files.get(type); + if (list != null && !list.isEmpty()) { + for (Iterator iter = list.iterator(); iter.hasNext();) { + File xmlFile = iter.next(); + try { + XMLPrettyPrinter.prettyPrintFile(xmlFile); + } catch (IOException e) { + throw new RuntimeException("Could not format XML file: " + xmlFile, e); + } + } + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/DefaultValueVisitor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/DefaultValueVisitor.java new file mode 100644 index 000000000000..8cf9582d7c3a --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/DefaultValueVisitor.java @@ -0,0 +1,141 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.common; + +import org.hibernate.mapping.Any; +import org.hibernate.mapping.Array; +import org.hibernate.mapping.Bag; +import org.hibernate.mapping.BasicValue; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.DependantValue; +import org.hibernate.mapping.IdentifierBag; +import org.hibernate.mapping.List; +import org.hibernate.mapping.ManyToOne; +import org.hibernate.mapping.Map; +import org.hibernate.mapping.OneToMany; +import org.hibernate.mapping.OneToOne; +import org.hibernate.mapping.PrimitiveArray; +import org.hibernate.mapping.Set; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.mapping.Value; +import org.hibernate.mapping.ValueVisitor; + +/** + * Default ValueVisitor which throws UnsupportedOperationException on all accepts. + * Can be changed by passing true to the constructor. + * + * @author max + * + */ +public class DefaultValueVisitor implements ValueVisitor { + + boolean throwException = true; + + /** + * + * @param throwException if true exception will be thrown, otherwise return null in accept calls. + */ + protected DefaultValueVisitor(boolean throwException) { + this.throwException = throwException; + } + + protected Object handle(Value o) { + if (throwException) { + + throw new UnsupportedOperationException("accept on " + o); + } + else { return null; } + } + + public Object accept(Bag o){ + + return handle(o); + } + + public Object accept(IdentifierBag o){ + + return handle(o); + } + + public Object accept(List o){ + + return handle(o); + } + + public Object accept(PrimitiveArray o){ + + return handle(o); + } + + public Object accept(Array o){ + + return handle(o); + } + + public Object accept(Map o){ + + return handle(o); + } + + public Object accept(OneToMany o){ + + return handle(o); + } + + public Object accept(Set o){ + + return handle(o); + } + + public Object accept(Any o){ + + return handle(o); + } + + public Object accept(SimpleValue o){ + + return handle(o); + } + + public Object accept(BasicValue o) { + + return handle(o); + + } + + public Object accept(DependantValue o){ + + return handle(o); + } + + public Object accept(Component o){ + + return handle(o); + } + + public Object accept(ManyToOne o){ + + return handle(o); + } + + public Object accept(OneToOne o){ + + return handle(o); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/EntityNameFromValueVisitor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/EntityNameFromValueVisitor.java new file mode 100644 index 000000000000..1b6c6d03e3f7 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/EntityNameFromValueVisitor.java @@ -0,0 +1,103 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.common; + +import org.hibernate.mapping.Array; +import org.hibernate.mapping.Bag; +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.IdentifierBag; +import org.hibernate.mapping.List; +import org.hibernate.mapping.ManyToOne; +import org.hibernate.mapping.Map; +import org.hibernate.mapping.OneToMany; +import org.hibernate.mapping.OneToOne; +import org.hibernate.mapping.PrimitiveArray; +import org.hibernate.mapping.Set; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.mapping.ToOne; + +public class EntityNameFromValueVisitor extends DefaultValueVisitor { + + public EntityNameFromValueVisitor() { + super( true ); + } + + public EntityNameFromValueVisitor(boolean b) { + super(b); + } + + public Object accept(OneToOne o) { + return acceptToOne(o); + } + + public Object accept(ManyToOne o) { + return acceptToOne(o); + } + + private Object acceptToOne(ToOne value) { + return value.getReferencedEntityName(); // should get the cfg and lookup the persistenclass. + } + + public Object accept(OneToMany value) { + return value.getAssociatedClass().getEntityName(); + } + + public Object acceptCollection(Collection c) { + return c.getElement().accept( this ); + } + + public Object accept(Bag o) { + return acceptCollection( o ); + } + + public Object accept(List o) { + return acceptCollection( o ); + } + + public Object accept(IdentifierBag o) { + return acceptCollection( o ); + } + + public Object accept(Set o) { + return acceptCollection( o ); + } + + public Object accept(Map o) { + return acceptCollection( o ); + } + + public Object accept(Array o) { + return acceptCollection( o ); + } + + public Object accept(PrimitiveArray o) { + return acceptCollection( o ); + } + + public Object accept(SimpleValue o) { + return null; // TODO: return o.getTypeName() ? (it is not an association) + } + + public Object accept(Component component) { + if(component.isDynamic()) { + return null; //"java.util.Map"; (not an association) + } + return component.getComponentClassName(); + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/ExporterSettings.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/ExporterSettings.java new file mode 100644 index 000000000000..5a8550922aa9 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/ExporterSettings.java @@ -0,0 +1,46 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.common; + +public interface ExporterSettings { + + public final String PREFIX_KEY = "hibernatetool."; + + /** + * if true exporters are allowed to generate EJB3 constructs + */ + public final String EJB3 = PREFIX_KEY + "ejb3"; + + /** + * if true then exporters are allowed to generate JDK 5 constructs + */ + public final String JDK5 = PREFIX_KEY + "jdk5"; + + /** + * the (root) output directory for an exporter + */ + public final String OUTPUT_DIRECTORY = PREFIX_KEY + "output_directory"; + + /** + * the (root) output directory for an exporter + */ + public final String TEMPLATE_PATH = PREFIX_KEY + "template_path"; + + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/GenericExporter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/GenericExporter.java new file mode 100644 index 000000000000..a50baac03c51 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/GenericExporter.java @@ -0,0 +1,185 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.common; + +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.Component; +import org.hibernate.tool.reveng.api.export.ExporterConstants; +import org.hibernate.tool.reveng.internal.export.java.ComponentPOJOClass; +import org.hibernate.tool.reveng.internal.export.java.POJOClass; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + + +public class GenericExporter extends AbstractExporter { + + static abstract class ModelIterator { + abstract void process(GenericExporter ge); + } + + static Map modelIterators = new HashMap(); + static { + modelIterators.put( "configuration", new ModelIterator() { + void process(GenericExporter ge) { + TemplateProducer producer = + new TemplateProducer( + ge.getTemplateHelper(), + ge.getArtifactCollector()); + producer.produce( + new HashMap(), + ge.getTemplateName(), + new File(ge.getOutputDirectory(),ge.getFilePattern()), + ge.getTemplateName(), + "Configuration"); + } + }); + modelIterators.put("entity", new ModelIterator() { + void process(GenericExporter ge) { + Iterator iterator = + ge.getCfg2JavaTool().getPOJOIterator( + ge.getMetadata().getEntityBindings().iterator()); + Map additionalContext = new HashMap(); + while ( iterator.hasNext() ) { + POJOClass element = (POJOClass) iterator.next(); + ge.exportPersistentClass( additionalContext, element ); + } + } + }); + modelIterators.put("component", new ModelIterator() { + + void process(GenericExporter ge) { + Map components = new HashMap(); + + Iterator iterator = + ge.getCfg2JavaTool().getPOJOIterator( + ge.getMetadata().getEntityBindings().iterator()); + Map additionalContext = new HashMap(); + while ( iterator.hasNext() ) { + POJOClass element = (POJOClass) iterator.next(); + ConfigurationNavigator.collectComponents(components, element); + } + + iterator = components.values().iterator(); + while ( iterator.hasNext() ) { + Component component = (Component) iterator.next(); + ComponentPOJOClass element = new ComponentPOJOClass(component,ge.getCfg2JavaTool()); + ge.exportComponent( additionalContext, element ); + } + } + }); + } + + protected String getTemplateName() { + return (String)getProperties().get(ExporterConstants.TEMPLATE_NAME); + } + + protected void doStart() { + + if(getFilePattern()==null) { + throw new RuntimeException("File pattern not set on " + this.getClass()); + } + if(getTemplateName()==null) { + throw new RuntimeException("Template name not set on " + this.getClass()); + } + + List exporters = new ArrayList(); + + if(StringHelper.isEmpty( getForEach() )) { + if(getFilePattern().indexOf("{class-name}")>=0) { + exporters.add( modelIterators.get( "entity" ) ); + exporters.add( modelIterators.get( "component") ); + } else { + exporters.add( modelIterators.get( "configuration" )); + } + } else { + StringTokenizer tokens = new StringTokenizer(getForEach(), ","); + + while ( tokens.hasMoreTokens() ) { + String nextToken = tokens.nextToken(); + ModelIterator modelIterator = modelIterators.get(nextToken); + if(modelIterator==null) { + throw new RuntimeException("for-each does not support [" + nextToken + "]"); + } + exporters.add(modelIterator); + } + } + + Iterator it = exporters.iterator(); + while(it.hasNext()) { + ModelIterator mit = it.next(); + mit.process( this ); + } + } + + protected void exportComponent(Map additionalContext, POJOClass element) { + exportPOJO(additionalContext, element); + } + + protected void exportPersistentClass(Map additionalContext, POJOClass element) { + exportPOJO(additionalContext, element); + } + + protected void exportPOJO(Map additionalContext, POJOClass element) { + TemplateProducer producer = new TemplateProducer(getTemplateHelper(),getArtifactCollector()); + additionalContext.put("pojo", element); + additionalContext.put("clazz", element.getDecoratedObject()); + String filename = resolveFilename( element ); + if(filename.endsWith(".java") && filename.indexOf('$')>=0) { + log.warn("Filename for " + getClassNameForFile( element ) + " contains a $. Innerclass generation is not supported."); + } + producer.produce( + additionalContext, + getTemplateName(), + new File(getOutputDirectory(),filename), + getTemplateName(), + element.toString()); + } + + protected String resolveFilename(POJOClass element) { + String filename = StringHelper.replace(getFilePattern(), "{class-name}", getClassNameForFile( element )); + String packageLocation = StringHelper.replace(getPackageNameForFile( element ),".", "/"); + if(StringHelper.isEmpty(packageLocation)) { + packageLocation = "."; // done to ensure default package classes doesn't end up in the root of the filesystem when outputdir="" + } + filename = StringHelper.replace(filename, "{package-name}", packageLocation); + return filename; + } + + protected String getPackageNameForFile(POJOClass element) { + return element.getPackageName(); + } + + protected String getClassNameForFile(POJOClass element) { + return element.getDeclarationName(); + } + + private String getFilePattern() { + return (String)getProperties().get(FILE_PATTERN); + } + + private String getForEach() { + return (String)getProperties().get(FOR_EACH); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/TemplateHelper.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/TemplateHelper.java new file mode 100644 index 000000000000..8d6e0fba6f5c --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/TemplateHelper.java @@ -0,0 +1,254 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.common; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.hibernate.tool.reveng.api.version.Version; +import org.jboss.logging.Logger; + +import freemarker.cache.ClassTemplateLoader; +import freemarker.cache.FileTemplateLoader; +import freemarker.cache.MultiTemplateLoader; +import freemarker.cache.TemplateLoader; +import freemarker.ext.beans.BeansWrapperBuilder; +import freemarker.template.Configuration; +import freemarker.template.SimpleDate; +import freemarker.template.SimpleHash; +import freemarker.template.Template; +import freemarker.template.TemplateDateModel; +import freemarker.template.TemplateModel; +import freemarker.template.TemplateModelException; + + +/** + * + * Helper and wrapper for a Template engine (currently only FreeMarker). + * Exposes only the essential functions to avoid too much coupling else where. + * + * @author max + * + */ +public class TemplateHelper { + + static final Logger log = Logger.getLogger(TemplateHelper.class); + + private File outputDirectory; + + protected Configuration freeMarkerEngine; + + protected SimpleHash context; + + public TemplateHelper() { + + } + + public void init(File outputDirectory, String[] templatePaths) { + this.outputDirectory = outputDirectory; + + context = new SimpleHash(new BeansWrapperBuilder(Configuration.VERSION_2_3_0).build()); + freeMarkerEngine = new Configuration(Configuration.VERSION_2_3_0); + + List loaders = new ArrayList<>(); + + for ( String templatePath : templatePaths ) { + File file = new File( templatePath ); + if ( file.exists() ) { + if ( file.isDirectory() ) { + try { + loaders.add( new FileTemplateLoader( file ) ); + } + catch (IOException e) { + throw new RuntimeException( "Problems with templatepath " + file, e ); + } + } + else if ( file.getName().endsWith( ".zip" ) || file.getName().endsWith( ".jar" ) ) { + final URLClassLoader classLoaderForZip; + try { + classLoaderForZip = new URLClassLoader( new URL[] {file.toURI().toURL()}, null ); + } + catch (MalformedURLException e) { + throw new RuntimeException( "template path " + file + " is not a valid zip file", e ); + } + + loaders.add( new ClassTemplateLoader( classLoaderForZip, "/" ) ); + } + else { + log.warn( "template path " + file + " is not a directory" ); + } + } + else { + log.warn( "template path " + file + " does not exist" ); + } + } + loaders.add(new ClassTemplateLoader(this.getClass(),"/")); // the template names are like pojo/Somewhere so have to be a rooted classpathloader + + freeMarkerEngine.setTemplateLoader(new MultiTemplateLoader( loaders.toArray( new TemplateLoader[0] ) )); + + } + + + public class Templates { + + public void createFile(String content, String fileName) { + Writer fw = null; + try { + fw = new BufferedWriter(new FileWriter(new File(getOutputDirectory(), fileName))); + fw.write(content); + } catch(IOException io) { + throw new RuntimeException("Problem when writing to " + fileName, io); + } finally { + if(fw!=null) { + try { + fw.flush(); + fw.close(); + } catch(IOException io ) { + //TODO: warn + } + } + } + } + } + + public File getOutputDirectory() { + return outputDirectory; + } + + + + public void putInContext(String key, Object value) { + if(value == null) throw new IllegalStateException("value must not be null for " + key); + Object replaced = internalPutInContext(key,value); + if(replaced!=null) { + log.warn( "Overwriting context: " + replaced + "."); + } + } + + public void removeFromContext(String key) { + Object replaced = internalRemoveFromContext(key); + if(replaced==null) throw new IllegalStateException(key + " did not exist in template context."); + } + + public void ensureExistence(File destination) { + // if the directory exists, make sure it is a directory + File dir = destination.getAbsoluteFile().getParentFile(); + if ( dir.exists() && !dir.isDirectory() ) { + throw new RuntimeException("The path: " + dir.getAbsolutePath() + " exists, but is not a directory"); + } // else make the directory and any non-existent parent directories + else if ( !dir.exists() ) { + if ( !dir.mkdirs() ) { + if(dir.getName().equals(".")) { // Workaround that Linux/JVM apparently can't handle mkdirs of File's with current dir references. + if(dir.getParentFile().mkdirs()) { + return; + } + } + throw new RuntimeException( "unable to create directory: " + dir.getAbsolutePath() ); + } + } + } + + protected SimpleHash getContext() { + return context; + } + + public void processString(String template, Writer output) { + + try { + Reader r = new StringReader(template); + Template t = new Template("unknown", r, freeMarkerEngine); + + t.process(getContext(), output); + } + catch (Exception e) { + throw new RuntimeException("Error while processing template string", e); + } + } + + public void setupContext() { + getContext().put("version", Version.versionString()); + getContext().put("ctx", getContext() ); //TODO: I would like to remove this, but don't know another way to actually get the list possible "root" keys for debugging. + getContext().put("templates", new Templates()); + + getContext().put("date", new SimpleDate(new Date(), TemplateDateModel.DATETIME)); + + } + + protected Object internalPutInContext(String key, Object value) { + TemplateModel model; + try { + model = getContext().get(key); + } + catch (TemplateModelException e) { + throw new RuntimeException("Could not get key " + key, e); + } + getContext().put(key, value); + return model; + } + + protected Object internalRemoveFromContext(String key) { + TemplateModel model; + try { + model = getContext().get(key); + } + catch (TemplateModelException e) { + throw new RuntimeException("Could not get key " + key, e); + } + getContext().remove(key); + return model; + } + + /** look up the template named templateName via the paths and print the content to the output */ + public void processTemplate(String templateName, Writer output, String rootContext) { + if(rootContext == null) { + rootContext = "Unknown context"; + } + + try { + Template template = freeMarkerEngine.getTemplate(templateName); + template.process(getContext(), output); + } + catch (Exception e) { + throw new RuntimeException("Error while processing " + rootContext + " with template " + templateName, e); + } + } + + + public boolean templateExists(String templateName) { + TemplateLoader templateLoader = freeMarkerEngine.getTemplateLoader(); + + try { + return templateLoader.findTemplateSource(templateName)!=null; + } + catch (IOException e) { + throw new RuntimeException("templateExists for " + templateName + " failed", e); + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/TemplateProducer.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/TemplateProducer.java new file mode 100644 index 000000000000..39a288fb81fc --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/common/TemplateProducer.java @@ -0,0 +1,117 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.common; + +import org.hibernate.tool.reveng.api.export.ArtifactCollector; +import org.jboss.logging.Logger; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.Map; +import java.util.Map.Entry; + + +public class TemplateProducer { + + private static final Logger log = Logger.getLogger(TemplateProducer.class); + private final TemplateHelper th; + private final ArtifactCollector ac; + + public TemplateProducer(TemplateHelper th, ArtifactCollector ac) { + this.th = th; + this.ac = ac; + } + + public void produce(Map additionalContext, String templateName, File destination, String identifier, String fileType, String rootContext) { + + String tempResult = produceToString( additionalContext, templateName, rootContext ); + + if( tempResult.trim().isEmpty() ) { + log.warn("Generated output is empty. Skipped creation for file " + destination); + return; + } + FileWriter fileWriter = null; + try { + + th.ensureExistence( destination ); + + ac.addFile(destination, fileType); + log.debug("Writing " + identifier + " to " + destination.getAbsolutePath() ); + fileWriter = new FileWriter(destination); + fileWriter.write(tempResult); + } + catch (Exception e) { + throw new RuntimeException("Error while writing result to file", e); + } finally { + if(fileWriter!=null) { + try { + fileWriter.flush(); + fileWriter.close(); + } + catch (IOException e) { + log.warn("Exception while flushing/closing " + destination,e); + } + } + } + + } + + + private String produceToString(Map additionalContext, String templateName, String rootContext) { + putInContext( th, additionalContext ); + StringWriter tempWriter = new StringWriter(); + BufferedWriter bw = new BufferedWriter(tempWriter); + // First run - writes to in-memory string + th.processTemplate(templateName, bw, rootContext); + removeFromContext( th, additionalContext ); + try { + bw.flush(); + } + catch (IOException e) { + throw new RuntimeException("Error while flushing to string",e); + } + return tempWriter.toString(); + } + + private void removeFromContext(TemplateHelper templateHelper, Map context) { + for ( Entry element : context.entrySet() ) { + templateHelper.removeFromContext( element.getKey() ); + } + } + + private void putInContext(TemplateHelper templateHelper, Map context) { + for ( Entry element : context.entrySet() ) { + templateHelper.putInContext( element.getKey(), element.getValue() ); + } + } + + public void produce(Map additionalContext, String templateName, File outputFile, String identifier) { + String fileType = outputFile.getName(); + fileType = fileType.substring(fileType.indexOf('.')+1); + produce(additionalContext, templateName, outputFile, identifier, fileType, null); + } + + public void produce(Map additionalContext, String templateName, File outputFile, String identifier, String rootContext) { + String fileType = outputFile.getName(); + fileType = fileType.substring(fileType.indexOf('.')+1); + produce(additionalContext, templateName, outputFile, identifier, fileType, rootContext); + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/dao/DaoExporter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/dao/DaoExporter.java new file mode 100644 index 000000000000..5e6ef4ed35aa --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/dao/DaoExporter.java @@ -0,0 +1,64 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.dao; + +import org.hibernate.tool.reveng.internal.export.java.JavaExporter; +import org.hibernate.tool.reveng.internal.export.java.POJOClass; + +import java.util.Map; + +public class DaoExporter extends JavaExporter { + + private static final String DAO_DAOHOME_FTL = "dao/daohome.ftl"; + + private String sessionFactoryName = "SessionFactory"; + + public DaoExporter() { + super(); + } + + protected void init() { + super.init(); + getProperties().put(TEMPLATE_NAME, DAO_DAOHOME_FTL); + getProperties().put(FILE_PATTERN, "{package-name}/{class-name}Home.java"); + } + + protected void exportComponent(Map additionalContext, POJOClass element) { + // noop - we dont want components + } + + public String getSessionFactoryName() { + return sessionFactoryName; + } + + public void setSessionFactoryName(String sessionFactoryName) { + this.sessionFactoryName = sessionFactoryName; + } + + protected void setupContext() { + getProperties().put("sessionFactoryName", getSessionFactoryName()); + super.setupContext(); + getTemplateHelper().putInContext("daoHelper", new DaoHelper()); + } + + public String getName() { + return "hbm2dao"; + } + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/dao/DaoHelper.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/dao/DaoHelper.java new file mode 100644 index 000000000000..dc2aed18cdd0 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/dao/DaoHelper.java @@ -0,0 +1,40 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2020-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.dao; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.query.NamedHqlQueryDefinition; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class DaoHelper { + + public static Iterable> getNamedHqlQueryDefinitions(Metadata metadata) { + List> result = new ArrayList>(); + metadata.visitNamedHqlQueryDefinitions(new Consumer>() { + @Override + public void accept(NamedHqlQueryDefinition t) { + result.add(t); + } + }); + return result; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/ddl/DdlExporter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/ddl/DdlExporter.java new file mode 100644 index 000000000000..65ce528fdb01 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/ddl/DdlExporter.java @@ -0,0 +1,168 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.ddl; + +import org.hibernate.boot.Metadata; +import org.hibernate.tool.hbm2ddl.SchemaExport; +import org.hibernate.tool.hbm2ddl.SchemaExport.Action; +import org.hibernate.tool.hbm2ddl.SchemaUpdate; +import org.hibernate.tool.reveng.internal.export.common.AbstractExporter; +import org.hibernate.tool.schema.TargetType; + +import java.io.File; +import java.util.EnumSet; +import java.util.Iterator; + +/** + * Schema Export (.ddl) code generation. + * + * @author Vitali + * + */ +public class DdlExporter extends AbstractExporter { + + protected void doStart() { + String outputFileName = getProperties().getProperty(OUTPUT_FILE_NAME); + Metadata metadata = getMetadata(); + final EnumSet targetTypes = EnumSet.noneOf( TargetType.class ); + if (getExportToConsole()) targetTypes.add(TargetType.STDOUT); + if (getExportToDatabase()) targetTypes.add(TargetType.DATABASE); + if (null != outputFileName) targetTypes.add(TargetType.SCRIPT); + if (getSchemaUpdate()) { + SchemaUpdate update = new SchemaUpdate(); + if(outputFileName == null && getDelimiter() == null && getHaltOnError() && getFormat()) { + update.execute(targetTypes, metadata); + } + else { + if (null != outputFileName) { + File outputFile = new File(getOutputDirectory(), outputFileName); + update.setOutputFile(outputFile.getPath()); + log.debug("delimiter ='"+ getDelimiter() + "'"); + update.setDelimiter(getDelimiter()); + update.setFormat(Boolean.valueOf(getFormat())); + } + + if (getHaltOnError()) { + update.setHaltOnError(Boolean.valueOf(getHaltOnError())); + } + + update.execute(targetTypes, metadata); + if (!update.getExceptions().isEmpty()) { + int i = 1; + for (Iterator iterator = update.getExceptions().iterator(); iterator + .hasNext(); i++) { + Throwable element = (Throwable) iterator.next(); + log.warn("Error #" + i + ": ", element); + + } + log.error(i - 1 + " errors occurred while performing Hbm2DDLExporter."); + if (getHaltOnError()) { + throw new RuntimeException( + "Errors while performing Hbm2DDLExporter"); + } + } + } + + } else { + SchemaExport export = new SchemaExport(); + if (null != outputFileName) { + export.setOutputFile(new File(getOutputDirectory(), + outputFileName).toString()); + } + if (null != getDelimiter()) { + export.setDelimiter(getDelimiter()); + } + export.setHaltOnError(getHaltOnError()); + export.setFormat(getFormat()); + if (getDrop() && getCreate()) { + export.execute(targetTypes, Action.BOTH, metadata); + } else if (getDrop()) { + export.execute(targetTypes, Action.DROP, metadata); + } else if (getCreate()) { + export.execute(targetTypes, Action.CREATE, metadata); + } else { + export.execute(targetTypes, Action.NONE, metadata); + } + } + + } + + private boolean getCreate() { + if (!getProperties().containsKey(CREATE_DATABASE)) { + return true; + } else { + return (boolean)getProperties().get(CREATE_DATABASE); + } + } + + private String getDelimiter() { + if (!getProperties().containsKey(DELIMITER)) { + return ";"; + } + return (String)getProperties().get(DELIMITER); + } + + private boolean getDrop() { + if (!getProperties().containsKey(DROP_DATABASE)) { + return false; + } else { + return (boolean)getProperties().get(DROP_DATABASE); + } + } + + private boolean getExportToConsole() { + if (!getProperties().containsKey(EXPORT_TO_CONSOLE)) { + return true; + } else { + return (boolean)getProperties().get(EXPORT_TO_CONSOLE); + } + } + + private boolean getExportToDatabase() { + if (!getProperties().containsKey(EXPORT_TO_DATABASE)) { + return true; + } else { + return (boolean)getProperties().get(EXPORT_TO_DATABASE); + } + } + + private boolean getFormat() { + if (!getProperties().containsKey(FORMAT)) { + return false; + } else { + return (boolean)getProperties().get(FORMAT); + } + } + + private boolean getHaltOnError() { + if (!getProperties().containsKey(HALT_ON_ERROR)) { + return false; + } else { + return (boolean)getProperties().get(HALT_ON_ERROR); + } + } + + private boolean getSchemaUpdate() { + if (!getProperties().containsKey(SCHEMA_UPDATE)) { + return false; + } else { + return (boolean)getProperties().get(SCHEMA_UPDATE); + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocExporter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocExporter.java new file mode 100644 index 000000000000..4d4950a54446 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocExporter.java @@ -0,0 +1,656 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.doc; + +import org.hibernate.HibernateException; +import org.hibernate.boot.Metadata; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterFactory; +import org.hibernate.tool.reveng.api.export.ExporterType; +import org.hibernate.tool.reveng.internal.export.common.AbstractExporter; +import org.hibernate.tool.reveng.internal.export.common.TemplateProducer; +import org.hibernate.tool.reveng.internal.export.java.POJOClass; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Exporter implementation that creates Hibernate Documentation. + * Creates Tables and Classes Details + * + * @author Ricardo C. Moral + * @author Amit Bhayani + * + */ +public class DocExporter extends AbstractExporter { + + /** + * CSS Definition. + */ + private static final String FILE_CSS_DEFINITION = "doc/doc-style.css"; + + /** + * Hibernate Image. + */ + private static final String FILE_HIBERNATE_IMAGE = "doc/hibernate_logo.gif"; + + /** + * Extends Image. + */ + private static final String FILE_EXTENDS_IMAGE = "doc/inherit.gif"; + + /** + * Main index page. + */ + private static final String FILE_INDEX = "doc/index.html"; + + /** + * Template used for the index of the table documentation. + */ + private static final String FTL_TABLES_INDEX = "doc/tables/index.ftl"; + + /** + * Template used for index of the entity documentation + */ + private static final String FTL_ENTITIES_INDEX = "doc/entities/index.ftl"; + + /** + * Template used for the Classes Summary + */ + private static final String FTL_ENTITIES_SUMMARY = "doc/entities/summary.ftl"; + + /** + * Template used for Class details + */ + private static final String FTL_ENTITIES_ENTITY = "doc/entities/entity.ftl"; + + /** + * Template used to create the Package List + */ + private static final String FTL_ENTITIES_PACKAGE_LIST = "doc/entities/package-list.ftl"; + + /** + * Template used to create the list of all Classes + */ + private static final String FTL_ENTITIES_ENTITY_LIST = "doc/entities/allEntity-list.ftl"; + + /** + * Template used to create List of Classes specific to packages. + */ + private static final String FTL_ENTITIES_PERPACKAGE_ENTITY_LIST = "doc/entities/perPackageEntity-list.ftl"; + + /** + * Template used to show the specific package details + */ + private static final String FTL_ENTITIES_PACKAGE_SUMMARY = "doc/entities/package-summary.ftl"; + + /** + * Template used for the Tables Summary. + */ + private static final String FTL_TABLES_SUMMARY = "doc/tables/summary.ftl"; + + /** + * Template used for table lists. + */ + private static final String FTL_TABLES_TABLE_LIST = "doc/tables/table-list.ftl"; + + /** + * Template used for table lists for a specific schema. + */ + private static final String FTL_TABLES_PERSCHEMA_TABLE_LIST = "doc/tables/schema-table-list.ftl"; + + /** + * Template used for schema lists. + */ + private static final String FTL_TABLES_SCHEMA_LIST = "doc/tables/schema-list.ftl"; + + /** + * Template used for Schema Summary. + */ + private static final String FTL_TABLES_SCHEMA_SUMMARY = "doc/tables/schema-summary.ftl"; + + /** + * Template used for the Table Details. + */ + private static final String FTL_TABLES_TABLE = "doc/tables/table.ftl"; + + /** + * Doc helper. + */ + private DocHelper docHelper; + + /** + * Doc File Manager. + */ + private DocFileManager docFileManager; + + public void doStart() { + generateCommmonAndAssets(); + + boolean graphsGenerated = generateDot(); + generateTablesIndex(); + generateTablesSummary(graphsGenerated); + generateTablesDetails(); + generateTablesAllSchemasList(); + generateTablesAllTablesList(); + generateTablesSchemaTableList(); + generateTablesSchemaDetailedInfo(); + + generateEntitiesIndex(); + generatePackageSummary(graphsGenerated); + generateEntitiesDetails(); + generateEntitiesAllPackagesList(); + generateEntitiesAllEntitiesList(); + generateEntitiesPackageEntityList(); + generateEntitiesPackageDetailedInfo(); + + + } + + private boolean generateDot() { + String cmd = getProperties().getProperty( "dot.executable" ); + boolean ignoreError = Boolean.parseBoolean(getProperties().getProperty("dot.ignoreerror", "false")); + + if(StringHelper.isNotEmpty( cmd )) { + try { + Exporter exporter = ExporterFactory.createExporter(ExporterType.GENERIC); + exporter.getProperties().putAll( getProperties() ); + exporter.getProperties().put(ARTIFACT_COLLECTOR, getArtifactCollector()); + exporter.getProperties().put(METADATA_DESCRIPTOR, getMetadataDescriptor()); + exporter.getProperties().put(DESTINATION_FOLDER, getOutputDirectory()); + String[] tp = (String[])exporter.getProperties().get(TEMPLATE_PATH); + if (tp != null) { + exporter.getProperties().put(TEMPLATE_PATH, tp); + } + + exporter.getProperties().put(TEMPLATE_NAME, "dot/entitygraph.dot.ftl"); + exporter.getProperties().put(FILE_PATTERN, "entities/entitygraph.dot"); + exporter.start(); + + exporter.getProperties().put(TEMPLATE_NAME, "dot/tablegraph.dot.ftl"); + exporter.getProperties().put(FILE_PATTERN, "tables/tablegraph.dot"); + exporter.start(); + + + File entityGraphDot = new File(getOutputDirectory(), "entities/entitygraph.dot"); + dotToFile( cmd, entityGraphDot.toString(), new File(getOutputDirectory(), "entities/entitygraph.png").toString()); + dotToFile( cmd, entityGraphDot.toString(), new File(getOutputDirectory(), "entities/entitygraph.svg").toString()); + dotToFile( cmd, entityGraphDot.toString(), new File(getOutputDirectory(), "entities/entitygraph.cmapx").toString()); + + File tableGraphDot = new File(getOutputDirectory(), "tables/tablegraph.dot"); + dotToFile( cmd, tableGraphDot.toString(), new File(getOutputDirectory(), "tables/tablegraph.png").toString()); + dotToFile( cmd, tableGraphDot.toString(), new File(getOutputDirectory(), "tables/tablegraph.svg").toString()); + dotToFile( cmd, tableGraphDot.toString(), new File(getOutputDirectory(), "tables/tablegraph.cmapx").toString()); + + return true; + + } + catch (IOException e) { + if(ignoreError) { + log.warn( "Skipping entitygraph creation since dot.executable was not found and dot.ignoreerror=false." ); + return false; + } else { + throw new HibernateException("Problem while generating DOT graph for Configuration (set dot.ignoreerror=false to ignore)", e); + } + } + } else { + log.info( "Skipping entitygraph creation since dot.executable is empty or not-specified." ); + return false; + } + } + + public static final String OS_NAME = System.getProperty("os.name"); + public static final boolean IS_LINUX = OS_NAME.startsWith("Linux"); + + private String escape(String fileName){ + + // Linux does not need " " around file names + if (IS_LINUX){ + return fileName; + } + + // Windows needs " " around file names; actually we do not + // need it always, only when spaces are present; + // but it does not hurt to usem them always + return "\"" + fileName + "\""; + + } + + private void dotToFile(String dotExeFileName, String dotFileName, String outFileName) throws IOException { + + // + // dot.exe works by taking *.dot file and piping + // results into another file, for example: + // d:\graphviz-1.12\bin\dot.exe -Tgif c:\temp\ManualDraw.dot > c:\temp\ManualDraw.gif + // so we follow that model here and read stdout until EOF + // + + final String[] cmdAsArray = new String[] { + escape( dotExeFileName ), + "-T", + getFormatForFile( outFileName ), + escape( dotFileName ), + "-o", + escape( outFileName ) + }; + + StringBuilder sb = new StringBuilder(); + for (String s : cmdAsArray) { + sb.append( s ).append( " " ); + } + + final String cmdAsString = sb.toString(); + + Process p = Runtime.getRuntime().exec(cmdAsArray); + //p.getErrorStream(). + try { + log.debug( "Executing: " + cmdAsString ); +// Get the input stream and read from it + InputStream in = p.getErrorStream(); + int c; + while ((c = in.read()) != -1) { + System.out.print((char)c); + } + in.close(); + int i = p.waitFor( ); + if(i!=0) { + //TODO: dump system.err + log.error("Error " + i + " while executing: " + cmdAsString); + } + } catch(Exception ie){ + log.error( "Error while executing: " + cmdAsString, ie ); + } + } + + private String getFormatForFile(String outFileName){ + int idx = outFileName.lastIndexOf("."); + if (idx == -1 || idx == outFileName.length() - 1){ + throw new IllegalArgumentException("Can't determine file name extention for file name " + outFileName); + } + return outFileName.substring(idx + 1); + } + + + protected void setupContext() { + if(!getProperties().contains( "jdk5" )) { + getProperties().setProperty( "jdk5", "true" ); + } + super.setupContext(); + Metadata metadata = getMetadata(); + docHelper = new DocHelper( metadata, getProperties(), getCfg2JavaTool() ); + docFileManager = new DocFileManager(docHelper, getOutputDirectory() ); + + getTemplateHelper().putInContext("dochelper", docHelper); + getTemplateHelper().putInContext("docFileManager", docFileManager); + } + + /** + * Generate common files and copy assets. + */ + public void generateCommmonAndAssets() { + try { + DocFile cssStylesDocFile = docFileManager.getCssStylesDocFile(); + + processTemplate(new HashMap(0), FILE_CSS_DEFINITION, cssStylesDocFile.getFile()); + + DocFile hibernateLogoDocFile = docFileManager.getHibernateImageDocFile(); + + DocFileManager.copy(this.getClass().getClassLoader(), FILE_HIBERNATE_IMAGE, + hibernateLogoDocFile.getFile() ); + + DocFile extendsImageDocFile = docFileManager.getExtendsImageDocFile(); + + DocFileManager.copy(this.getClass().getClassLoader(), FILE_EXTENDS_IMAGE, extendsImageDocFile.getFile()); + + DocFile mainIndexDocFile = docFileManager.getMainIndexDocFile(); + + processTemplate(new HashMap(0), FILE_INDEX, mainIndexDocFile.getFile() ); + } + catch (IOException ioe) { + throw new RuntimeException("Error while copying files.", ioe); + } + } + + /** + * Generate the index file of the table documentation. + */ + public void generateTablesIndex() { + DocFile docFile = docFileManager.getTableIndexDocFile(); + + File file = docFile.getFile(); + + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + + processTemplate(parameters, FTL_TABLES_INDEX, file); + } + + /** + * Generate the index file of the class documentation + */ + public void generateEntitiesIndex(){ + DocFile docFile = docFileManager.getClassIndexDocFile(); + File file = docFile.getFile(); + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + processTemplate(parameters, FTL_ENTITIES_INDEX, file ); + } + + /** + * Generate a file with an summary of all the tables. + * @param graphsGenerated + */ + public void generateTablesSummary(boolean graphsGenerated) { + DocFile docFile = docFileManager.getTableSummaryDocFile(); + + File file = docFileManager.getTableSummaryDocFile().getFile(); + + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + parameters.put( "graphsGenerated", Boolean.valueOf( graphsGenerated ) ); + if(graphsGenerated) { + StringBuffer sb = new StringBuffer(); + String fileName = "tables/tablegraph.cmapx"; + appendFile( sb, fileName ); + parameters.put( "tablegrapharea", sb ); + } + + processTemplate(parameters, FTL_TABLES_SUMMARY, file); + } + + private void appendFile(StringBuffer sb, String fileName) { + try { + BufferedReader in = new BufferedReader(new FileReader(new File(getOutputDirectory(), fileName))); + String str; + + while ((str = in.readLine()) != null) { + sb.append(str); + sb.append(System.getProperty("line.separator")); + } + + in.close(); + } catch (IOException e) { + } + } + + /** + * Generate summary (summaty.html) to show all the packages + * + */ + public void generatePackageSummary(boolean graphsGenerated){ + DocFile docFile = docFileManager.getClassSummaryFile(); + File file = docFile.getFile(); + + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + + List list = docHelper.getPackages(); + if (list.size() > 0){ + //Remove All Classes + list.remove(0); + } + parameters.put("packageList", list ); + parameters.put( "graphsGenerated", Boolean.valueOf( graphsGenerated ) ); + if(graphsGenerated) { + StringBuffer sb = new StringBuffer(); + String fileName = "entities/entitygraph.cmapx"; + appendFile( sb, fileName ); + parameters.put( "entitygrapharea", sb ); + } + + processTemplate(parameters, FTL_ENTITIES_SUMMARY, file); + } + + /** + * Generate one file per table with detail information. + */ + public void generateTablesDetails() { + Metadata metadata = getMetadata(); + Iterator
tables = metadata.collectTableMappings().iterator(); + while (tables.hasNext() ) { + Table table = tables.next(); + + DocFile docFile = docFileManager.getTableDocFile(table); + if(docFile!=null) { + File file = docFile.getFile(); + + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + parameters.put("table", table); + + processTemplate(parameters, FTL_TABLES_TABLE, file); + } + } + } + + /** + * generates one html file for each class containing detail information of class + * + */ + public void generateEntitiesDetails(){ + Iterator classes = docHelper.getClasses().iterator(); + while(classes.hasNext()){ + POJOClass pcObj = classes.next(); + + pcObj.getPropertiesForMinimalConstructor(); + DocFile docFile = docFileManager.getEntityDocFile(pcObj); + File file = docFile.getFile(); + + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + parameters.put("class", pcObj); + processTemplate(parameters, FTL_ENTITIES_ENTITY, file); + } + } + + /** + * Generates the html file containig list of packages (allpackages.html) + * + */ + public void generateEntitiesAllPackagesList() { + DocFile docFile = docFileManager.getAllPackagesDocFile(); + + File file = docFile.getFile(); + + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + List list = docHelper.getPackages(); + if (list.size() > 0){ + //Remove All Classes + list.remove(0); + } + parameters.put("packageList", list ); + + processTemplate(parameters, FTL_ENTITIES_PACKAGE_LIST, file); + } + + /** + * Generates the html file containing list of classes (allclases.html) + * + */ + public void generateEntitiesAllEntitiesList() { + DocFile docFile = docFileManager.getAllEntitiesDocFile(); + + File file = docFile.getFile(); + + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + parameters.put("classList", docHelper.getClasses()); + + processTemplate(parameters, FTL_ENTITIES_ENTITY_LIST, file); + } + + /** + * generates the list of classes sepcific to package + * + */ + public void generateEntitiesPackageEntityList() { + Iterator packages = docHelper.getPackages().iterator(); + + while (packages.hasNext() ) { + String packageName = packages.next(); + + if(!packageName.equals(DocHelper.DEFAULT_NO_PACKAGE)){ + DocFile docFile = docFileManager.getPackageEntityListDocFile(packageName); + File file = docFile.getFile(); + + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + parameters.put("title", packageName); + parameters.put("classList", docHelper.getClasses(packageName)); + processTemplate(parameters, FTL_ENTITIES_PERPACKAGE_ENTITY_LIST, file); + + } + + } + } + + /** + * Generates the html file containing list of classes and interfaces for given package + * + */ + public void generateEntitiesPackageDetailedInfo() { + List packageList = docHelper.getPackages(); + if (packageList.size() > 0){ + //Remove All Classes + packageList.remove(0); + } + Iterator packages = packageList.iterator(); + + while (packages.hasNext() ) { + String packageName = packages.next(); + + DocFile summaryDocFile = docFileManager.getPackageSummaryDocFile(packageName); + Map parameters = new HashMap(); + parameters.put("docFile", summaryDocFile); + parameters.put("package", packageName); + parameters.put("classList", docHelper.getClasses(packageName)); + + processTemplate(parameters, FTL_ENTITIES_PACKAGE_SUMMARY, + summaryDocFile.getFile() ); + } + } + + /** + * Generate a file with a list of all the schemas in the configuration. + */ + public void generateTablesAllSchemasList() { + DocFile docFile = docFileManager.getAllSchemasDocFile(); + + File file = docFile.getFile(); + + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + parameters.put("schemaList", docHelper.getSchemas() ); + + processTemplate(parameters, FTL_TABLES_SCHEMA_LIST, file); + } + + /** + * Generate a file with a list of all the tables in the configuration. + */ + public void generateTablesAllTablesList() { + DocFile docFile = docFileManager.getAllTablesDocFile(); + + File file = docFile.getFile(); + + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + parameters.put("tableList", docHelper.getTables() ); + + processTemplate(parameters, FTL_TABLES_TABLE_LIST, file); + } + + public void generateTablesSchemaTableList() { + Iterator schemas = docHelper.getSchemas().iterator(); + + while (schemas.hasNext() ) { + String schemaName = schemas.next(); + + DocFile docFile = docFileManager.getSchemaTableListDocFile(schemaName); + + File file = docFile.getFile(); + + Map parameters = new HashMap(); + parameters.put("docFile", docFile); + parameters.put("title", schemaName); + parameters.put("tableList", docHelper.getTables(schemaName) ); + + processTemplate(parameters, FTL_TABLES_PERSCHEMA_TABLE_LIST, file); + } + } + + /** + * Generate two files per schema. One with a summary of the tables in the + * schema and another one with a list of tables. + */ + public void generateTablesSchemaDetailedInfo() { + Iterator schemas = docHelper.getSchemas().iterator(); + while (schemas.hasNext() ) { + String schemaName = schemas.next(); + + DocFile summaryDocFile = docFileManager.getSchemaSummaryDocFile(schemaName); + + Map parameters = new HashMap(); + parameters.put("docFile", summaryDocFile); + parameters.put("schema", schemaName); + + processTemplate(parameters, FTL_TABLES_SCHEMA_SUMMARY, + summaryDocFile.getFile() ); + + DocFile tableListDocFile = docFileManager.getSchemaSummaryDocFile(schemaName); + + parameters = new HashMap(); + parameters.put("docFile", tableListDocFile); + parameters.put("schema", schemaName); + + processTemplate(parameters, FTL_TABLES_SCHEMA_SUMMARY, + tableListDocFile.getFile() ); + + //processTemplate( new HashMap(), templateName, outputFile ); + } + } + + /** + * Run templates. + * + * @param parameters the parameters to pass to the templates template. + * @param templateName the template to use. + * @param outputFile the output file. + */ + protected void processTemplate(Map parameters, String templateName, + File outputFile) { + + TemplateProducer producer = new TemplateProducer(getTemplateHelper(), getArtifactCollector() ); + producer.produce(parameters, templateName, outputFile, templateName); + } + + public String getName() { + return "hbm2doc"; + } + +} \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFile.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFile.java new file mode 100644 index 000000000000..a202d59c5811 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFile.java @@ -0,0 +1,182 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.doc; + +import java.io.File; +import java.util.List; + +/** + * Represents a documentation file. + * + * @author Ricardo C. Moral + */ +public class DocFile { + + /** + * The name of the file. + */ + final private String name; + + /** + * The parent folder. + */ + final private DocFolder parentFolder; + + /** + * The File representation. + */ + final private File file; + + /** + * Constructor. + * + * @param pName the name of the file. + * @param pFolder the parent folder. + * + * @throws IllegalArgumentException if one of the parameters is null. + */ + public DocFile(String pName, DocFolder pFolder) { + super(); + + if (pName == null) { + throw new IllegalArgumentException("The name cannot be null"); + } + + if (pFolder == null) { + throw new IllegalArgumentException( + "The parent folder cannot be null"); + } + + name = pName; + parentFolder = pFolder; + + file = new File(parentFolder.getFile(), pName); + } + + /** + * Returns the name of the file. + * + * @return the name of the file. + */ + public String getName() { + + return name; + } + + /** + * Return the parent DocFolder. + * + * @return the DocFolder. + */ + public DocFolder getFolder() { + + return parentFolder; + } + + /** + * Returns the File representation. + * + * @return the File. + */ + public File getFile() { + + return file; + } + + /** + * Returns a list with the folders from root. + * + * @return a list with the folders from root. + */ + public List getPathFolders() { + + return parentFolder.getPathFolders(); + } + + /** + * Return a path-like reference to this file starting on the specified + * folder. The folder must be a parent folder. + * + * @param folder the folder. + * + * @return a path-like reference string. + */ + private String buildRefFromFolder(DocFolder folder) { + + StringBuffer result = new StringBuffer(); + + List folders = getPathFolders(); + + int index = folders.indexOf(folder); + + if (index == -1) { + throw new IllegalArgumentException( + "The specified folder is not on this file's path: " + + folder); + } + + for (index++; index < folders.size(); index++) { + DocFolder f = (DocFolder) folders.get(index); + result.append(f.getName() + '/'); + } + + result.append(getName() ); + + return result.toString(); + } + + /** + * Return a path-like reference to the specified file. + * + * @param target the target file. + * + * @return a path-like reference string. + */ + public String buildRefTo(DocFile target) { + + List tgtFileFolders = target.getPathFolders(); + + StringBuffer ref = new StringBuffer(); + + DocFolder localFolder = this.parentFolder; + while (localFolder != null) { + if (tgtFileFolders.contains(localFolder) ) { + ref.append(target.buildRefFromFolder(localFolder) ); + String result = ref.toString(); + return result; + } + else { + ref.append("../"); + localFolder = localFolder.getParent(); + } + } + + throw new IllegalArgumentException("No parent folder in common"); + } + + /** + * Return a String representation of this file. + * + * @return a String. + */ + public String toString() { + return name; + } + +} + diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFileManager.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFileManager.java new file mode 100644 index 000000000000..d789b8cef9b2 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFileManager.java @@ -0,0 +1,512 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.doc; + +import org.hibernate.mapping.Table; +import org.hibernate.tool.reveng.internal.export.java.POJOClass; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +/** + * Class used to manage the files created during the documentation generation + * process. This manager is needed to manage references between files. + * + * @author Ricardo C. Moral + * @author Amit Bhayani + */ +public class DocFileManager { + + /** + * Root Documentation Folder. + */ + private final DocFolder rootDocFolder; + + /** + * The main index file for the documentation. + */ + private final DocFile mainIndexDocFile; + + /** + * Folder for the utility files. + */ + private final DocFolder assetsDocFolder; + + /** + * The Hibernate image. + */ + private final DocFile hibernateImageDocFile; + + /** + * The extends image. + */ + private final DocFile extendsImageDocFile; + + /** + * The CSS stylesheet file. + */ + private final DocFile cssStylesDocFile; + + /** + * Class index DocFile + */ + private final DocFile classIndexDocFile; + + /** + * Class Summary DocFile + */ + private final DocFile entitySummaryDocFile; + + /** + * All packages DocFile allpackages.html + */ + private final DocFile allPackagesDocFile; + + /** + * All classes DocFile allclases.html + */ + private final DocFile allEntitiesDocFile; + + /** + * Table index DocFile. + */ + private final DocFile tableIndexDocFile; + + /** + * Table summary DocFile. + */ + private final DocFile tableSummaryDocFile; + + /** + * All Schemas DocFile. + */ + private final DocFile allSchemasDocFile; + + /** + * All Tables DocFile. + */ + private final DocFile allTablesDocFile; + + /** + * Map with the doc files for the tables. The keys are the Table objects and + * the values are the DocFile instances. + */ + private final Map tableDocFiles = new HashMap(); + + /** + * Map with the DocFile for classes. The keys are the POJOClass objects and + * the values are the DocFile instances. + */ + private final Map entityDocFiles = new HashMap(); + + /** + * Map with the schema summary DocFiles keyed by Schema FQN. + */ + private final Map schemaSummaryDocFiles = new HashMap(); + + /** + * Map with the package summary DocFiles keyed by package name + */ + private final Map packageSummaryDocFiles = new HashMap(); + + /** + * Map with the schema table lists DocFiles keyed by Schema FQN. + */ + private final Map schemaTableListDocFiles = new HashMap(); + + /** + * Map with package class lists DocFiles keyed by package name + */ + private final Map packageEntityListDocFile = new HashMap(); + + public DocFolder getRootDocFolder() { + return rootDocFolder; + } + + /** + * Constructor. + * + * @param docHelper the doc helper. + * @param pRootFolder the root folder for the documentation. + */ + public DocFileManager(DocHelper docHelper, File pRootFolder) { + + super(); + + rootDocFolder = new DocFolder(pRootFolder); + + mainIndexDocFile = new DocFile("index.html", rootDocFolder); + + assetsDocFolder = new DocFolder("assets", rootDocFolder); + + hibernateImageDocFile = new DocFile("hibernate_logo.gif", + assetsDocFolder); + + extendsImageDocFile = new DocFile("inherit.gif", assetsDocFolder); + + cssStylesDocFile = new DocFile("doc-style.css", assetsDocFolder); + + DocFolder rootEntitiesDocFolder = new DocFolder( "entities", rootDocFolder ); + classIndexDocFile = new DocFile("index.html", rootEntitiesDocFolder ); + entitySummaryDocFile = new DocFile("summary.html", rootEntitiesDocFolder ); + allPackagesDocFile = new DocFile("allpackages.html", rootEntitiesDocFolder ); + allEntitiesDocFile = new DocFile("allentities.html", rootEntitiesDocFolder ); + + + DocFolder rootTablesDocFolder = new DocFolder( "tables", rootDocFolder ); + + tableIndexDocFile = new DocFile("index.html", rootTablesDocFolder ); + + tableSummaryDocFile = new DocFile("summary.html", rootTablesDocFolder ); + + allSchemasDocFile = new DocFile("allschemas.html", rootTablesDocFolder ); + + allTablesDocFile = new DocFile("alltables.html", rootTablesDocFolder ); + + for ( String packageName : docHelper.getPackages() ) { + DocFolder packageFolder = null; + DocFolder theRoot = rootEntitiesDocFolder; + if ( !packageName.equals( DocHelper.DEFAULT_NO_PACKAGE ) ) { + String[] packagesArr = packageName.split( "\\." ); + + for ( String s : packagesArr ) { + packageFolder = new DocFolder( s, theRoot ); + theRoot = packageFolder; + } + + DocFile packageSummaryDocFile = new DocFile( "summary.html", packageFolder ); + packageSummaryDocFiles.put( packageName, packageSummaryDocFile ); + + DocFile classListDocFile = new DocFile( "entities.html", packageFolder ); + packageEntityListDocFile.put( packageName, classListDocFile ); + } + else { + packageFolder = rootEntitiesDocFolder; + } + + for ( POJOClass pc : docHelper.getClasses( packageName ) ) { + String classFileName = pc.getDeclarationName(); + classFileName = classFileName + ".html"; + DocFile classDocFile = new DocFile( classFileName, packageFolder ); + entityDocFiles.put( pc, classDocFile ); + } + } + + for ( String schemaName : docHelper.getSchemas() ) { + DocFolder schemaFolder = new DocFolder( schemaName, + rootTablesDocFolder ); + DocFile schemaSummaryDocFile = new DocFile( "summary.html", + schemaFolder ); + schemaSummaryDocFiles.put( schemaName, schemaSummaryDocFile ); + DocFile tableListDocFile = new DocFile( "tables.html", schemaFolder ); + schemaTableListDocFiles.put( schemaName, tableListDocFile ); + + for ( Table table : docHelper.getTables( schemaName ) ) { + if ( table.isPhysicalTable() ) { + String tableFileName = table.getName() + ".html"; + + DocFile tableDocFile = new DocFile( tableFileName, schemaFolder ); + + tableDocFiles.put( table, tableDocFile ); + } + } + } + } + + /** + * Returns the DocFolder for the helper files. + * + * @return the value. + */ + public DocFolder getAssetsDocFolder() { + + return assetsDocFolder; + } + + /** + * Returns the DocFile for the CSS definitions. + * + * @return the value. + */ + public DocFile getCssStylesDocFile() { + + return cssStylesDocFile; + } + + /** + * Returns the DocFile for the Hibernate Image. + * + * @return the value. + */ + public DocFile getHibernateImageDocFile() { + + return hibernateImageDocFile; + } + + /** + * Returns the DocFile for the extends Image. + * + * @return the value. + */ + public DocFile getExtendsImageDocFile() { + + return extendsImageDocFile; + } + + /** + * Returns the DocFile for the main index. + * + * @return the value. + */ + public DocFile getMainIndexDocFile() { + + return mainIndexDocFile; + } + + /** + * Return the table index DocFile. + * + * @return the table index DocFile. + */ + public DocFile getTableIndexDocFile() { + return tableIndexDocFile; + } + + /** + * Returns the class index DocFile + * @return class index DocFile + */ + public DocFile getClassIndexDocFile(){ + return classIndexDocFile; + } + + /** + * Returns the summary index DocFile + * @return summary index DocFile + */ + public DocFile getClassSummaryFile(){ + return entitySummaryDocFile; + } + + /** + * Returns the DocFile responsible for generating allpackages.html + * @return DocFile + */ + public DocFile getAllPackagesDocFile(){ + return allPackagesDocFile; + } + + /** + * Returns the DocFile responsible for generating allclasses.html + */ + public DocFile getAllEntitiesDocFile(){ + return allEntitiesDocFile; + } + + /** + * Returns the DocFile responsible to generate classes.html corresponding to packageName passed + * @param packageName Package name which acts as key to get DocFile value object from packageEntityListDocFile + * @return DocFile for classes.html + */ + public DocFile getPackageEntityListDocFile(String packageName){ + return packageEntityListDocFile.get(packageName); + + } + + /** + * Return the table summary DocFile. + * + * @return the table summary DocFile. + */ + public DocFile getTableSummaryDocFile() { + return tableSummaryDocFile; + } + + /** + * Return the all schemas DocFile. + * + * @return the all schemas DocFile. + */ + public DocFile getAllSchemasDocFile() { + return allSchemasDocFile; + } + + /** + * Return the all tables DocFile. + * + * @return the all tables DocFile. + */ + public DocFile getAllTablesDocFile() { + return allTablesDocFile; + } + + /** + * Return the DocFile for the specified Table. + * + * @param table the Table. + * + * @return the DocFile. + */ + public DocFile getTableDocFile(Table table) { + return tableDocFiles.get(table); + } + + + /** + * Get the DocFile corresponding to POJOClass. But if the POJOClass is ComponentPOJO, it is created on fly + * and we are not implementing .equals method hence get by getQualifiedDeclarationName. + * @param pc DocFile corresponding to this POJOClass + * @return DocFile + */ + public DocFile getEntityDocFileByDeclarationName(POJOClass pc){ + DocFile df = getEntityDocFile(pc); + String pcQualifiedDeclarationName = pc.getQualifiedDeclarationName(); + String pojoClassQualifiedDeclarationName ; + //TODO Can we implement equals method in BasicPOJO to avoid this loop? + if(df == null){ + for ( POJOClass pojoClass : entityDocFiles.keySet() ) { + pojoClassQualifiedDeclarationName = pojoClass.getQualifiedDeclarationName(); + + if ( pcQualifiedDeclarationName.equals( pojoClassQualifiedDeclarationName ) ) { + df = entityDocFiles.get( pojoClass ); + break; + } + } + + + + } + return df; + } + + /** + * Returns the DocFile responsible to generate the .html for each classes. + * @param pc The DocFile corresponding to this pc is retrieved from entityDocFiles + * @return DocFile + */ + public DocFile getEntityDocFile(POJOClass pc){ + return entityDocFiles.get(pc); + } + + /** + * Return the summary DocFile for the specified schema FQN. + * + * @param schemaName the name of the schema. + * + * @return the DocFile. + */ + public DocFile getSchemaSummaryDocFile(String schemaName) { + return schemaSummaryDocFiles.get(schemaName); + } + + /** + * get DocFile responsible to generate summary.html for corresponding packageName passed + * @param packageName DocFile corresponding to this packagename is retrieved from packageSummaryDocFiles + * @return DocFile + */ + public DocFile getPackageSummaryDocFile(String packageName){ + return packageSummaryDocFiles.get(packageName); + } + + /** + * Return the Table List DocFile for the specified schema FQN. + * + * @param schemaName the name of the schema. + * + * @return the DocFile. + */ + public DocFile getSchemaTableListDocFile(String schemaName) { + return schemaTableListDocFiles.get(schemaName); + } + + /** + * Return the relative reference between the specified files. + * + * @param from the origin. + * @param to the target. + * + * @throws IllegalArgumentException if any parameter is null. + */ + public String getRef(DocFile from, DocFile to) { + if (from == null) { + throw new IllegalArgumentException("From cannot be null."); + } + + if (to == null) { + throw new IllegalArgumentException("To cannot be null."); + } + + return from.buildRefTo(to); + } + + /** + * Copy a File. + * + * TODO: this method ignores custom provided templatepath. Want to call freemarker to get the resourceloaders but they are hidden, so we need another way. + * ..and if we use currentthread classloader you might conflict with the projects tools.jar + * + * @param fileName the name of the file to copy. + * @param to the target file. + * + * @throws IOException in case of error. + */ + public static void copy(ClassLoader loader, String fileName, File to) throws IOException { + InputStream is = null; + FileOutputStream out = null; + try { + /*ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = DocFileManager.class.getClassLoader(); + } + + is = classLoader.getResourceAsStream(fileName);*/ + + /*if (is == null && classLoader!=DocFileManager.class.getClassLoader() ) { + is = DocFileManager.class.getClassLoader().getResourceAsStream(fileName); // HACK: workaround since eclipse for some reason doesnt provide the right classloader; + + } */ + is = loader.getResourceAsStream( fileName ); + + if(is==null) { + throw new IllegalArgumentException("File not found: " + + fileName); + } + + out = new FileOutputStream(to); + + int value; + while ( (value = is.read() ) != -1) { + out.write(value); + } + + } + finally { + if (is != null) { + is.close(); + } + if (out != null) { + out.close(); + } + } + } + +} \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFolder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFolder.java new file mode 100644 index 000000000000..73250cbed911 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocFolder.java @@ -0,0 +1,151 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.doc; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a documentation folder. + * + * @author Ricardo C. Moral + */ +public class DocFolder { + + /** + * The name of the folder. + */ + private String name; + + /** + * The parent folder. + */ + private DocFolder parent; + + /** + * The File instance. + */ + private final File file; + + /** + * Holds a list with the folders that are between this folder and root. + */ + private final List pathFolders = new ArrayList<>(); + + /** + * Constructor for the root folder. + */ + public DocFolder(File root) { + super(); + + if (root == null) { + throw new IllegalArgumentException("Root File cannot be null"); + } + + file = root; + + pathFolders.add(this); + } + + /** + * Constructor. + * + * @param pName the name of the file. + * @param pParent the parent folder. + */ + public DocFolder(String pName, DocFolder pParent) { + super(); + + if (pName == null) { + throw new IllegalArgumentException("Name cannot be null"); + } + + if (pParent == null) { + throw new IllegalArgumentException("Parent folder cannot be null"); + } + + name = pName; + parent = pParent; + + file = new File(parent.getFile(), name); + + if (file.exists() ) { + if (!file.isDirectory() ) { + throw new RuntimeException("The path: " + + file.getAbsolutePath() + + " exists, but is not a Folder"); + } + } + else { + if (!file.mkdirs() ) { + throw new RuntimeException("unable to create folder: " + + file.getAbsolutePath() ); + } + } + pathFolders.addAll(parent.getPathFolders() ); + pathFolders.add(this); + } + + /** + * Return the name of this folder. + * + * @return the name of this folder. + */ + public String getName() { + return name; + } + + /** + * Returns the parent folder. + * + * @return the parent folder. + */ + public DocFolder getParent() { + return parent; + } + + /** + * Return the File instance. + * + * @return the File instance. + */ + public File getFile() { + return file; + } + + /** + * Returns a list with the folders from root. + * + * @return a list with the folders from root. + */ + public List getPathFolders() { + return pathFolders; + } + + /** + * Return a String representation of this folder. + * + * @return a String. + */ + public String toString() { + return name; + } + +} + diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocHelper.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocHelper.java new file mode 100644 index 000000000000..9179a8222f5f --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/doc/DocHelper.java @@ -0,0 +1,508 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.doc; + +import org.hibernate.HibernateException; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Table; +import org.hibernate.mapping.Value; +import org.hibernate.tool.reveng.internal.export.common.ConfigurationNavigator; +import org.hibernate.tool.reveng.internal.export.java.Cfg2JavaTool; +import org.hibernate.tool.reveng.internal.export.java.ComponentPOJOClass; +import org.hibernate.tool.reveng.internal.export.java.POJOClass; +import org.hibernate.tool.reveng.internal.core.binder.TypeUtils; +import org.hibernate.type.Type; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * This helper class is used expose hibernate mapping information to the + * templates. + * + * @author Ricardo C. Moral + * @author Amit Bhayani + */ +public final class DocHelper { + + /** used to sort pojoclass according to their declaration name */ + static final Comparator POJOCLASS_COMPARATOR = new Comparator() { + public int compare(POJOClass left, POJOClass right) { + return left.getDeclarationName().compareTo(right.getDeclarationName()); + } + }; + + /** + * Used to sort properties according to their name. + */ + private static final Comparator PROPERTY_COMPARATOR = new Comparator() { + public int compare(Property left, Property right) { + return left.getName().compareTo(right.getName()); + } + }; + + /** + * Name to use if the schema is not specified. + */ + public static final String DEFAULT_NO_SCHEMA_NAME = "default"; + + /** + * Name to use if there are no packages specified for any class + */ + public static final String DEFAULT_NO_PACKAGE = "All Entities"; + + /** + * Map with Tables keyed by Schema FQN. The keys are Strings and the values + * are Lists of Tables + */ + private final Map> tablesBySchema = + new HashMap>(); + + /** + * Map with classes keyed by package name. PackageName is String key and + * values are List of POJOClass + */ + private final Map> classesByPackage = + new HashMap>(); + + /** + * Lits of all POJOClass + */ + private final List classes = + new ArrayList(); + + /** + * Map where the keys are column names (tableFQN.column) and the values are + * lists with the Value instances where those columns referenced. + */ + private final Map> valuesByColumn = + new HashMap>(); + + /** + * Holds intances of Property keyed by Value objects. + */ + private final Map> propsByValue = + new HashMap>(); + + /** + * List with all the tables. + */ + private final List
tables = new ArrayList
(); + + /** + * Map that holds the Schema FQN for each Table. The keys are Table + * instances and the values are Strings with the Schema FQN for that table. + */ + private final Map tableSchemaNames = new HashMap(); + + private final Metadata metadata; + + public DocHelper(Metadata metadata, Properties properties, Cfg2JavaTool cfg2JavaTool) { + + super(); + + if (metadata == null) { + throw new IllegalArgumentException("Hibernate Configuration cannot be null"); + } + + this.metadata = metadata; + + StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder(); + builder.applySettings(properties); + String defaultCatalog = properties.getProperty(AvailableSettings.DEFAULT_CATALOG); + String defaultSchema = properties.getProperty(AvailableSettings.DEFAULT_SCHEMA); + if (defaultSchema == null) { + defaultSchema = DEFAULT_NO_SCHEMA_NAME; + } + + for (Table table : metadata.collectTableMappings()) { + if (!table.isPhysicalTable()) { + continue; + } + tables.add(table); + + StringBuilder sb = new StringBuilder(); + + String catalog = table.getCatalog(); + if (catalog == null) { + catalog = defaultCatalog; + } + if (catalog != null) { + sb.append(catalog).append("."); + } + + String schema = table.getSchema(); + if (schema == null) { + schema = defaultSchema; + } + + sb.append(schema); + + String qualSchemaName = sb.toString(); + + tableSchemaNames.put(table, qualSchemaName); + + List
tableList = tablesBySchema.computeIfAbsent(qualSchemaName, k -> new ArrayList<>()); + tableList.add(table); + + for (Column column : table.getColumns()) { + String columnFQN = getQualifiedColumnName(table, column); + List values = valuesByColumn.computeIfAbsent(columnFQN, k -> new ArrayList<>()); + values.add(column.getValue()); + } + } + + Map components = new HashMap(); + + for (PersistentClass clazz : metadata.getEntityBindings()) { + POJOClass pojoClazz = cfg2JavaTool.getPOJOClass(clazz); + ConfigurationNavigator.collectComponents(components, pojoClazz); + + this.processClass(pojoClazz); + + for (Property property : clazz.getProperties()) { + Value value = property.getValue(); + List props = propsByValue.computeIfAbsent(value, k -> new ArrayList<>()); + props.add(property); + } + } + + for (Component component : components.values()) { + ComponentPOJOClass element = + new ComponentPOJOClass(component, cfg2JavaTool); + this.processClass(element); + } + } + + /** + * Populate classes List and classesByPackage Map + */ + private void processClass(POJOClass pojoClazz) { + + classes.add(pojoClazz); + String packageName = pojoClazz.getPackageName(); + + if (packageName == null || packageName.isEmpty()) { + packageName = DEFAULT_NO_PACKAGE; + } + + List classList = classesByPackage.computeIfAbsent(packageName, k -> new ArrayList()); + classList.add(pojoClazz); + } + + /** + * Return a Map with the tables keyed by Schema. The keys are the schema + * names and the values are Lists of tables. + * + * @return a Map with the tables keyed by Schema Name. + */ + public Map> getTablesBySchema() { + return tablesBySchema; + } + + /** + * return a Map which has List of POJOClass as value keyed by package name + * as String. + */ + public Map> getClassesByPackage() { + return classesByPackage; + } + + /** + * Returns a list with all the schemas. + * + * @return a list with all the schemas. + */ + public List getSchemas() { + List schemas = new ArrayList(tablesBySchema.keySet()); + Collections.sort(schemas); + return schemas; + } + + /** + * Return a sorted List of packages + */ + public List getPackages() { + List packages = new ArrayList(classesByPackage.keySet()); + Collections.sort(packages); + return packages; + } + + /** + * Return the list of tables for a particular schema. + * + * @param schema + * the name of the schema. + * + * @return a list with all the tables. + */ + public List
getTables(String schema) { + return tablesBySchema.get(schema); + } + + /** + * return a sorted List of POJOClass corresponding to packageName passed + * + * @param packageName + * packageName other than DEFAULT_NO_PACKAGE + * @return a sorted List of POJOClass + */ + public List getClasses(String packageName) { + List clazzes = classesByPackage.get(packageName); + List orderedClasses = new ArrayList(clazzes); + orderedClasses.sort(POJOCLASS_COMPARATOR); + return orderedClasses; + } + + /** + * Return all the tables. + * + * @return all the tables. + */ + public List
getTables() { + return tables; + } + + /** + * Return a sorted List of all POJOClass + */ + public List getClasses() { + List orderedClasses = new ArrayList(classes); + orderedClasses.sort(POJOCLASS_COMPARATOR); + return orderedClasses; + } + + /** + * Returns the qualified schema name for a table. The qualified schema name + * will include the catalog name if one is specified. + * + * @param table + * the table. + * + * @return the qualified schema name for the table. + */ + public String getQualifiedSchemaName(Table table) { + + return (String) tableSchemaNames.get(table); + } + + /** + * Returns the qualified name of a table. + * + * @param table + * the table. + * + * @return the qualified name of the table. + */ + public String getQualifiedTableName(Table table) { + + String qualifiedSchemaName = getQualifiedSchemaName(table); + + return qualifiedSchemaName + '.' + table.getName(); + } + + public String getPropertyType(Property p) { + Value v = p.getValue(); + Type t; + String propertyString = "N/D"; + try { + t = v.getType(); + propertyString = t.getReturnedClass().getName(); + + } catch (Exception ex) { + // TODO we should try to get the value from value here + // Eat Exception?? + } + + return propertyString; + } + + /** + * Returns the qualified name of a column. + * + * @param table + * the table. + * @param column + * the column + * + * @return the FQN of the column. + */ + public String getQualifiedColumnName(Table table, Column column) { + String qualifiedTableName = getQualifiedTableName(table); + return qualifiedTableName + '.' + column.getName(); + } + + /** + * Get the SQL type name for a column. + * + * @param column + * the column. + * + * @return a String with the SQL type name. + */ + public String getSQLTypeName(Column column) { + + try { + return column.getSqlType(metadata); + } catch (HibernateException ex) { + + // TODO: Fix this when we find a way to get the type or + // the mapping. + + return "N/D"; + } + } + + public int getLength(Column column) { + return column.getLength() == null ? + TypeUtils.DEFAULT_COLUMN_LENGTH : + column.getLength().intValue(); + } + + public int getPrecision(Column column) { + return column.getPrecision() == null ? + TypeUtils.DEFAULT_COLUMN_PRECISION : + column.getPrecision(); + } + + public int getScale(Column column) { + return column.getScale() == null ? + TypeUtils.DEFAULT_COLUMN_SCALE : + column.getScale(); + } + + public Iterator getPrimaryKeyColumnIterator(Table table) { + return table.getPrimaryKey().getColumns().iterator(); + } + + /** + * Returns the values that use the specified column. + * + * @param table + * the table. + * @param column + * the column. + * + * @return a list with the values. + */ + public List getValues(Table table, Column column) { + String columnFQN = getQualifiedColumnName(table, column); + List values = valuesByColumn.get(columnFQN); + if (values != null) { + return values; + } else { + return new ArrayList(); + } + } + + /** + * Returns the properties that map to a column. + * + * @param table + * the table. + * @param column + * the column. + * + * @return a list of properties. + */ + public List getProperties(Table table, Column column) { + + List result = new ArrayList(); + for (Value value : getValues(table, column)) { + List props = propsByValue.get(value); + if (props != null) { + result.addAll(props); + } + } + return result; + } + + /** + * Method used in class.vm template to get the ComponentPOJO class + * corresponding to Property if its of Type Component. + * + * @param property + * Get ComponentPOJO corresponding to this Property + * @return POJOClass for Property + */ + // TODO We haven't taken into account Array? + public POJOClass getComponentPOJO(Property property) { + if (property.getValue() instanceof Component comp) { + return new ComponentPOJOClass(comp, new Cfg2JavaTool()); + } else { + return null; + } + } + + public List getInheritanceHierarchy(POJOClass pc) { + if (pc.isSubclass()) { + List superClasses = new ArrayList(); + POJOClass superClass = pc.getSuperClass(); + while (superClass != null) { + superClasses.add(superClass); + superClass = superClass.getSuperClass(); + } + return superClasses; + } else { + return Collections.emptyList(); + } + } + + public List getOrderedProperties(POJOClass pojoClass) { + List orderedProperties = getAllProperties(pojoClass); + orderedProperties.sort(PROPERTY_COMPARATOR); + + return orderedProperties; + } + + public List getSimpleProperties(POJOClass pojoClass) { + List properties = getAllProperties(pojoClass); + if (pojoClass.hasIdentifierProperty()) + properties.remove(pojoClass.getIdentifierProperty()); + // TODO: do we need to also remove component id properties? + if (pojoClass.hasVersionProperty()) + properties.remove(pojoClass.getVersionProperty()); + return properties; + } + + public List getOrderedSimpleProperties(POJOClass pojoClass) { + List orderedProperties = getSimpleProperties(pojoClass); + orderedProperties.sort(PROPERTY_COMPARATOR); + return orderedProperties; + } + + private List getAllProperties(POJOClass pojoClass) { + List properties = new ArrayList(); + for (Iterator iterator = pojoClass.getAllPropertiesIterator(); iterator.hasNext();) + properties.add(iterator.next()); + return properties; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/Cfg2HbmTool.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/Cfg2HbmTool.java new file mode 100644 index 000000000000..c33776cde236 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/Cfg2HbmTool.java @@ -0,0 +1,478 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.hbm; + +import org.hibernate.FetchMode; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.query.NamedHqlQueryDefinition; +import org.hibernate.boot.query.NamedNativeQueryDefinition; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.OptimisticLockStyle; +import org.hibernate.engine.spi.FilterDefinition; +import org.hibernate.id.PersistentIdentifierGenerator; +import org.hibernate.mapping.Any; +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.Formula; +import org.hibernate.mapping.JoinedSubclass; +import org.hibernate.mapping.ManyToOne; +import org.hibernate.mapping.OneToMany; +import org.hibernate.mapping.OneToOne; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.PersistentClassVisitor; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.RootClass; +import org.hibernate.mapping.Selectable; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.mapping.SingleTableSubclass; +import org.hibernate.mapping.Subclass; +import org.hibernate.mapping.UnionSubclass; +import org.hibernate.mapping.Value; +import org.hibernate.persister.entity.JoinedSubclassEntityPersister; +import org.hibernate.persister.entity.SingleTableEntityPersister; +import org.hibernate.persister.entity.UnionSubclassEntityPersister; +import org.hibernate.persister.spi.PersisterClassResolver; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.tool.reveng.internal.export.common.EntityNameFromValueVisitor; +import org.hibernate.tool.reveng.internal.core.util.EnhancedValue; +import org.hibernate.tool.reveng.internal.util.SkipBackRefPropertyIterator; +import org.hibernate.tool.reveng.internal.util.ValueUtil; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.function.Consumer; + +/** + * @author David Channon and Max + */ +public class Cfg2HbmTool { + + private static final class HasEntityPersisterVisitor implements PersistentClassVisitor { + private final String name; + + private HasEntityPersisterVisitor(String name) { + this.name = name; + } + + public Object accept(Subclass subclass) { + return bool(!SingleTableEntityPersister.class.getName().equals(name)); + } + + private Object bool(boolean b) { + return b; + } + + public Object accept(JoinedSubclass subclass) { + return bool(!JoinedSubclassEntityPersister.class.getName().equals(name)); + } + + public Object accept(SingleTableSubclass subclass) { + return bool(!SingleTableEntityPersister.class.getName().equals(name)); + } + + public Object accept(UnionSubclass subclass) { + return bool(!UnionSubclassEntityPersister.class.getName().equals(name)); + } + + public Object accept(RootClass class1) { + return bool(!SingleTableEntityPersister.class.getName().equals(name)); + } + } + + /** + * Remove any internal keys from the set, eg, any Keys that are prefixed by + * 'target_' and return the filtered collection. + */ + public static Properties getFilteredIdentifierGeneratorProperties(Properties properties, Properties environmentProperties) { + if (properties != null){ + Properties fProp = new Properties(); + for (Object o : properties.keySet()) { + String key = (String) o; + if ("schema".equals(key)) { + String schema = properties.getProperty(key); + if (!isDefaultSchema(schema, environmentProperties)) { + fProp.put(key, schema); + } + } else if ("catalog".equals(key)) { + String catalog = properties.getProperty(key); + if (!isDefaultCatalog(catalog, environmentProperties)) { + fProp.put(key, catalog); + } + } else if (!key.startsWith("target_")) { + fProp.put(key, properties.get(key)); + } + } + return fProp; + } + return null; + } + + static private boolean isDefaultSchema(String schema, Properties properties) { + String defaultSchema = properties.getProperty(Environment.DEFAULT_SCHEMA); + return defaultSchema == null ? schema == null : defaultSchema.equals(schema); + } + + static private boolean isDefaultCatalog(String catalog, Properties properties) { + String defaultCatalog = properties.getProperty(Environment.DEFAULT_CATALOG); + return defaultCatalog == null ? catalog == null : defaultCatalog.equals(catalog); + } + + public String getTag(PersistentClass pc) { + return (String) pc.accept(HBMTagForPersistentClassVisitor.INSTANCE); + } + + public String getTag(Property property) { + PersistentClass persistentClass = property.getPersistentClass(); + if(persistentClass!=null) { + if(persistentClass.getVersion()==property) { + String typeName = ((SimpleValue)property.getValue()).getTypeName(); + if("timestamp".equals(typeName) || "dbtimestamp".equals(typeName)) { + return "timestamp"; + } else { + return "version"; + } + } + } + String toolTag = (String) property.getValue().accept(HBMTagForValueVisitor.INSTANCE); + if ("component".equals(toolTag) && "embedded".equals(property.getPropertyAccessorName())){ + toolTag = "properties"; + } + return toolTag; + } + + public String getCollectionElementTag(Property property){ + Value value = property.getValue(); + if (isOneToMany(value)) return "one-to-many"; + if (isManyToMany(value)) return "many-to-many"; + if (isManyToAny(value)) return "many-to-any"; + if (((Collection)value).getElement() instanceof Component){ + return "composite"; + } + return "element"; + } + + + public boolean isUnsavedValue(Property property) { + SimpleValue sv = (SimpleValue) property.getValue(); + return (sv.getNullValue() != null) && !"undefined".equals(sv.getNullValue()); + } + + public String getUnsavedValue(Property property) { + return ( (SimpleValue) property.getValue() ).getNullValue(); + } + + public boolean isIdentifierGeneratorProperties(Property property) { + Properties val = this.getIdentifierGeneratorProperties(property); + return val != null; + } + + public Properties getIdentifierGeneratorProperties(Property property) { + Properties result = null; + SimpleValue simpleValue = (SimpleValue)property.getValue(); + if (simpleValue instanceof EnhancedValue) { + Properties idGenParams = ((EnhancedValue)simpleValue).getIdentifierGeneratorProperties(); + if (idGenParams != null) { + result = new Properties(); + result.putAll(idGenParams); + } + } else { + Map properties = new ValueUtil(simpleValue).getIdentifierGeneratorParameters(); + if (properties != null) { + result = new Properties(); + result.putAll(properties); + } + } + return result; + } + + public Set getFilteredIdentifierGeneratorKeySet(Property property, Properties props) { + return getFilteredIdentifierGeneratorProperties(this.getIdentifierGeneratorProperties(property), props).keySet(); + } + + public boolean isOneToMany(Property property) { + return isOneToMany(property.getValue()); + } + + public boolean isOneToMany(Value value) { + if(value instanceof Collection) { + return ( (Collection)value ).isOneToMany(); + } + else + return value instanceof OneToMany; + } + + public boolean isManyToMany(Property property) { + return isManyToMany(property.getValue()); + } + + public boolean isManyToMany(Value value) { + return (value instanceof Collection && + ((Collection)value).getElement() instanceof ManyToOne); + } + + + public boolean isCollection(Property property) { + return property.getValue() instanceof Collection; + } + + public boolean isOneToManyCollection(Property property) { + return isCollection(property) && ((Collection)property.getValue()).isOneToMany(); + } + + public boolean isSimpleValue(Property property) { + return (property.getValue() instanceof SimpleValue); + } + + public boolean isManyToOne(Property property) { + return isManyToOne(property.getValue()); + } + + public boolean isManyToAny(Property property) { + return isManyToAny(property.getValue()); + } + + public boolean isManyToAny(Value value) { + return (value instanceof Collection && + ((Collection)value).getElement() instanceof Any); + } + + public boolean isManyToOne(Value value) { + return (value instanceof ManyToOne); + } + + public boolean isOneToOne(Property property) { + return (property.getValue() instanceof OneToOne); + } + + public boolean isTemporalValue(Property property) { + if(property.getValue() instanceof SimpleValue) { + String typeName = ((SimpleValue)property.getValue()).getTypeName(); + if("date".equals(typeName) || "java.sql.Date".equals(typeName)) { + return true; + } else if ("timestamp".equals(typeName) || "java.sql.Timestamp".equals(typeName)) { + return true; + } else return "time".equals(typeName) || "java.sql.Time".equals(typeName); + } + return false; + } + + public boolean isNamedQueries(Metadata md) { + final ArrayList> list = new ArrayList>(); + Consumer> consumer = new Consumer>() { + @Override + public void accept(NamedHqlQueryDefinition namedHqlQueryDefinition) { + list.add(namedHqlQueryDefinition); + } + }; + md.visitNamedHqlQueryDefinitions(consumer); + return !list.isEmpty(); + } + + public boolean isNamedSQLQueries(Metadata md) { + final ArrayList> list = new ArrayList>(); + Consumer> consumer = new Consumer>() { + @Override + public void accept(NamedNativeQueryDefinition namedHqlQueryDefinition) { + list.add(namedHqlQueryDefinition); + } + }; + md.visitNamedNativeQueryDefinitions(consumer); + return !list.isEmpty(); + } + + + public String getCollectionLazy(Collection value){ + return value.isExtraLazy() ? "extra" : Boolean.toString(value.isLazy()); + } + + public boolean isFilterDefinitions(Metadata md) { + Map filterdefs = md.getFilterDefinitions(); + return filterdefs != null && !filterdefs.isEmpty(); + } + + public boolean isClassLevelOptimisticLockMode(PersistentClass pc) { + return pc.getOptimisticLockStyle() != OptimisticLockStyle.VERSION; + } + + public String getClassLevelOptimisticLockMode(PersistentClass pc) { + OptimisticLockStyle oMode = pc.getOptimisticLockStyle(); + if ( oMode == OptimisticLockStyle.DIRTY ) { + return "dirty"; + } + else if ( oMode == OptimisticLockStyle.ALL ) { + return "all"; + } + else if ( oMode == OptimisticLockStyle.NONE ) { + return "none"; + } + else { + return "version"; + } + } + + public boolean hasFetchMode(Property property) { + String fetch = getFetchMode(property); + return fetch != null && !"default".equals(fetch); + } + public String getFetchMode(Property property) { + FetchMode fetchMode = property.getValue().getFetchMode(); + return (fetchMode== null) ? null : fetchMode.toString().toLowerCase(); + } + + + public Formula getFormulaForProperty(Property prop) { + for (Selectable selectable : prop.getValue().getSelectables()) { + if (selectable instanceof Formula) + return (Formula) selectable; + } + return null; + } + + public String columnAttributes(Column col) { + return columnAttributes(col, false); + } + + public String columnAttributes(Column column, boolean isPrimaryKeyColumn) { + StringBuilder sb = new StringBuilder(); + if (column.getPrecision() != null) { + sb.append("precision=\"").append(column.getPrecision() ).append("\" "); + } + if (column.getScale() != null) { + sb.append("scale=\"").append(column.getScale() ).append("\" "); + } + else if (column.getLength() != null){ + sb.append("length=\"").append(column.getLength() ).append("\" "); + } + if (!isPrimaryKeyColumn) { + if (!column.isNullable() ) { + sb.append("not-null=\"true\" "); + } + if (column.isUnique() ) { + sb.append("unique=\"true\" "); + } + } + if (column.getSqlType() != null) { + sb.append("sql-type=\""); sb.append(column.getSqlType() ); sb.append("\" "); + } + return sb.toString(); + } + + public String getClassName(PersistentClass pc) { + if (pc.hasPojoRepresentation() ) { + return pc.getClassName(); + } + else { + // todo: return null? + throw new RuntimeException(pc + " does not have a pojo rep."); + } + } + + public String getClassName(OneToMany om) { + return om.getAssociatedClass().getClassName(); + } + + public String getProxyInterfaceName(PersistentClass pc) { + if (pc.hasPojoRepresentation() ) { + return pc.getProxyInterfaceName(); + } + else { + throw new RuntimeException(pc + " does not have a pojo rep."); + } + } + + public boolean isImportData(Metadata md) { + return !(md.getImports().isEmpty()); + } + + public boolean needsDiscriminatorElement(PersistentClass clazz) { + return clazz instanceof RootClass + && (clazz.getDiscriminator() != null); + } + + public boolean needsDiscriminator(PersistentClass clazz) { + + return clazz instanceof Subclass + && !(clazz instanceof UnionSubclass) && !(clazz instanceof JoinedSubclass); + } + + + public boolean needsTable(PersistentClass clazz) { + + return (Boolean) clazz.accept(new PersistentClassVisitor(){ + + public Object accept(Subclass subclass) { + return Boolean.FALSE; + } + + public Object accept(JoinedSubclass subclass) { + return Boolean.TRUE; + } + + public Object accept(SingleTableSubclass subclass) { + return Boolean.FALSE; + } + + public Object accept(UnionSubclass subclass) { + return Boolean.TRUE; + } + + public Object accept(RootClass class1) { + return Boolean.TRUE; + } + }); + } + + public boolean isSubclass(PersistentClass clazz) { + return clazz instanceof org.hibernate.mapping.Subclass; + } + + public boolean isJoinedSubclass(PersistentClass clazz) { + return clazz instanceof JoinedSubclass; + } + + public boolean hasCustomEntityPersister(PersistentClass clazz) { + ServiceRegistry sr = clazz.getServiceRegistry(); + PersisterClassResolver pcr = sr.getService(PersisterClassResolver.class); + if (pcr == null) return false; + Class entityPersisterClass = pcr.getEntityPersisterClass(clazz); + if(entityPersisterClass==null) return false; + final String name = entityPersisterClass.getName(); + return (Boolean) clazz.accept(new HasEntityPersisterVisitor(name)); + } + + public String getHibernateTypeName(Property p) { + return (String) p.getValue().accept(new EntityNameFromValueVisitor()); + } + + + public String getSafeHibernateTypeName(Property p) { + return (String) p.getValue().accept(new EntityNameFromValueVisitor(false)); + } + + public Iterator getProperties(Component v) { + return new SkipBackRefPropertyIterator(v.getProperties().iterator()); + } + + public Iterator getProperties(PersistentClass pc) { + return new SkipBackRefPropertyIterator(pc.getProperties().iterator()); + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HBMTagForPersistentClassVisitor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HBMTagForPersistentClassVisitor.java new file mode 100644 index 000000000000..c70689a281cd --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HBMTagForPersistentClassVisitor.java @@ -0,0 +1,61 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.hbm; + +import org.hibernate.mapping.JoinedSubclass; +import org.hibernate.mapping.PersistentClassVisitor; +import org.hibernate.mapping.RootClass; +import org.hibernate.mapping.SingleTableSubclass; +import org.hibernate.mapping.Subclass; +import org.hibernate.mapping.UnionSubclass; + +/** + * @author max + * + */ +public class HBMTagForPersistentClassVisitor implements PersistentClassVisitor { + + public static final PersistentClassVisitor INSTANCE = new HBMTagForPersistentClassVisitor(); + + protected HBMTagForPersistentClassVisitor() { + + } + + public Object accept(RootClass class1) { + return "class"; + } + + public Object accept(UnionSubclass subclass) { + return "union-subclass"; + } + + public Object accept(SingleTableSubclass subclass) { + return "subclass"; + } + + public Object accept(JoinedSubclass subclass) { + return "joined-subclass"; + } + + public Object accept(Subclass subclass) { + return "subclass"; + } + + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HBMTagForValueVisitor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HBMTagForValueVisitor.java new file mode 100644 index 000000000000..9cfe0f2c90e9 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HBMTagForValueVisitor.java @@ -0,0 +1,104 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.hbm; + +import org.hibernate.mapping.Any; +import org.hibernate.mapping.Array; +import org.hibernate.mapping.Bag; +import org.hibernate.mapping.BasicValue; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.DependantValue; +import org.hibernate.mapping.IdentifierBag; +import org.hibernate.mapping.List; +import org.hibernate.mapping.ManyToOne; +import org.hibernate.mapping.Map; +import org.hibernate.mapping.OneToMany; +import org.hibernate.mapping.OneToOne; +import org.hibernate.mapping.PrimitiveArray; +import org.hibernate.mapping.Set; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.tool.reveng.internal.export.common.DefaultValueVisitor; + +public class HBMTagForValueVisitor extends DefaultValueVisitor { + + public static final HBMTagForValueVisitor INSTANCE = new HBMTagForValueVisitor(); + + protected HBMTagForValueVisitor() { + super(true); + } + + public Object accept(Bag bag) { + return "bag"; + } + + public Object accept(IdentifierBag bag) { + return "idbag"; + } + + public Object accept(List list) { + return "list"; + } + + public Object accept(Map map) { + return "map"; + } + + public Object accept(OneToMany many) { + return "one-to-many"; + } + + public Object accept(Set set) { + return "set"; + } + + public Object accept(Any any) { + return "any"; + } + + public Object accept(SimpleValue value) { + return "property"; + } + + public Object accept(BasicValue value) { + return "property"; + } + + public Object accept(PrimitiveArray primitiveArray) { + return "primitive-array"; + } + + public Object accept(Array list) { + return "array"; + } + + public Object accept(DependantValue value) { + throw new IllegalArgumentException("No tag for " + value); + } + + public Object accept(Component component) { + return component.isDynamic()?"dynamic-component":"component"; + } + + public Object accept(ManyToOne mto) { + return "many-to-one"; + } + + public Object accept(OneToOne oto) { + return "one-to-one"; + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HbmExporter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HbmExporter.java new file mode 100644 index 000000000000..29500ec423e8 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HbmExporter.java @@ -0,0 +1,89 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.hbm; + +import org.hibernate.boot.Metadata; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.tool.reveng.internal.export.common.GenericExporter; +import org.hibernate.tool.reveng.internal.export.common.TemplateProducer; +import org.hibernate.tool.reveng.internal.export.java.POJOClass; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +/** + * @author david and max + */ +public class HbmExporter extends GenericExporter { + + protected HibernateMappingGlobalSettings globalSettings = new HibernateMappingGlobalSettings(); + + protected void setupContext() { + super.setupContext(); + getTemplateHelper().putInContext("hmgs", globalSettings); + } + + public void setGlobalSettings(HibernateMappingGlobalSettings hgs) { + this.globalSettings = hgs; + } + + public void doStart() { + exportGeneralSettings(); + super.doStart(); + } + + private void exportGeneralSettings() { + Cfg2HbmTool c2h = getCfg2HbmTool(); + Metadata md = getMetadata(); + if( c2h.isImportData(md) && + (c2h.isNamedQueries(md)) && + (c2h.isNamedSQLQueries(md)) && + (c2h.isFilterDefinitions(md))) { + TemplateProducer producer = new TemplateProducer(getTemplateHelper(),getArtifactCollector()); + producer.produce(new HashMap(), "hbm/generalhbm.hbm.ftl", new File(getOutputDirectory(),"GeneralHbmSettings.hbm.xml"), getTemplateName(), "General Settings"); + } + } + + protected void init() { + getProperties().put(TEMPLATE_NAME, "hbm/hibernate-mapping.hbm.ftl"); + getProperties().put(FILE_PATTERN, "{package-name}/{class-name}.hbm.xml"); + } + + public HbmExporter() { + init(); + } + + protected String getClassNameForFile(POJOClass element) { + return StringHelper.unqualify(((PersistentClass)element.getDecoratedObject()).getEntityName()); + } + + protected String getPackageNameForFile(POJOClass element) { + return StringHelper.qualifier(((PersistentClass)element.getDecoratedObject()).getClassName()); + } + + + protected void exportComponent(Map additionalContext, POJOClass element) { + // we don't want component's exported. + } + + public String getName() { + return "hbm2hbmxml"; + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HibernateMappingGlobalSettings.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HibernateMappingGlobalSettings.java new file mode 100644 index 000000000000..2918dff0b130 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/hbm/HibernateMappingGlobalSettings.java @@ -0,0 +1,163 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.hbm; + +import org.hibernate.internal.util.StringHelper; + +/** + * This class replicates the global settings that can be selected + * within the mapping document. This is provided to allow a GUI + * too to choose these settings and thus the generated mapping + * document will include them. + * + * @author David Channon + */ +public class HibernateMappingGlobalSettings { + + private String schemaName; + private String catalogName; + private String defaultCascade; + private String defaultPackage; + private String defaultAccess; + private boolean autoImport = true; + private boolean defaultLazy = true; + + /** + */ + public HibernateMappingGlobalSettings() { + } + + public boolean hasNonDefaultSettings() { + return this.hasDefaultPackage() || + this.hasSchemaName() || + this.hasCatalogName() || + this.hasNonDefaultCascade()|| + this.hasNonDefaultAccess() || + !this.isDefaultLazy() || + !this.isAutoImport() + ; + } + + public boolean hasDefaultPackage() { + return !StringHelper.isEmpty(defaultPackage); + } + + public boolean hasSchemaName() { + return !StringHelper.isEmpty(schemaName); + } + + public boolean hasCatalogName() { + return !StringHelper.isEmpty(catalogName); + } + + public boolean hasNonDefaultCascade() { + return !StringHelper.isEmpty(defaultCascade) ? !"none".equals(defaultCascade) ? true : false : false; + } + + public boolean hasNonDefaultAccess() { + return !StringHelper.isEmpty(defaultAccess) ? !"property".equals(defaultAccess) ? true : false : false; + } + + public String getSchemaName() { + return schemaName; + } + + public String getCatalogName() { + return catalogName; + } + + public String getDefaultCascade() { + return defaultCascade; + } + + public String getDefaultAccess() { + return defaultAccess; + } + + public String getDefaultPackage() { + return defaultPackage; + } + + public boolean isDefaultLazy() { + return defaultLazy; + } + + /** + * Returns the autoImport. + * @return boolean + */ + public boolean isAutoImport() { + return autoImport; + } + + /** + * Sets the schemaName. + * @param schemaName The schemaName to set + */ + public void setSchemaName(String schemaName) { + this.schemaName = schemaName; + } + + /** + * Sets the catalogName. + * @param catalogName The catalogName to set + */ + public void setCatalogName(String catalogName) { + this.catalogName = catalogName; + } + + /** + * Sets the defaultCascade. + * @param defaultCascade The defaultCascade to set + */ + public void setDefaultCascade(String defaultCascade) { + this.defaultCascade = defaultCascade; + } + + /** + * sets the default access strategy + * @param defaultAccess the default access strategy. + */ + public void setDefaultAccess(String defaultAccess) { + this.defaultAccess = defaultAccess; + } + + /** + * @param defaultPackage The defaultPackage to set. + */ + public void setDefaultPackage(String defaultPackage) { + this.defaultPackage = defaultPackage; + } + + /** + * Sets the autoImport. + * @param autoImport The autoImport to set + */ + public void setAutoImport(boolean autoImport) { + this.autoImport = autoImport; + } + + /** + * Sets the defaultLazy. + * @param defaultLazy The defaultLazy to set + */ + public void setDefaultLazy(boolean defaultLazy) { + this.defaultLazy = defaultLazy; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/BasicPOJOClass.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/BasicPOJOClass.java new file mode 100644 index 000000000000..00b6f0b644e8 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/BasicPOJOClass.java @@ -0,0 +1,977 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.Array; +import org.hibernate.mapping.Bag; +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.IdentifierBag; +import org.hibernate.mapping.MetaAttributable; +import org.hibernate.mapping.MetaAttribute; +import org.hibernate.mapping.PrimitiveArray; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Selectable; +import org.hibernate.mapping.Set; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.tool.reveng.internal.export.common.DefaultValueVisitor; +import org.hibernate.tool.reveng.internal.util.NameConverter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.hibernate.tool.reveng.internal.export.java.MetaAttributeConstants.CLASS_DESCRIPTION; +import static org.hibernate.tool.reveng.internal.export.java.MetaAttributeConstants.SCOPE_CLASS; +import static org.hibernate.tool.reveng.internal.export.java.MetaAttributeConstants.CLASS_MODIFIER; +import static org.hibernate.tool.reveng.internal.export.java.MetaAttributeConstants.INTERFACE; + +/** + * Abstract implementation of POJOClass. To be extended by ComponentPOJO and EntityPOJO + * @author max + * @author Amit Bhayani + * + */ +abstract public class BasicPOJOClass implements POJOClass { + + protected ImportContext importContext; + protected MetaAttributable meta; + protected final Cfg2JavaTool c2j; + + public BasicPOJOClass(MetaAttributable ma, Cfg2JavaTool c2j) { + this.meta = ma; + this.c2j = c2j; + + if(this.meta==null) { + throw new IllegalArgumentException("class Argument must be not null"); + } + if(this.c2j==null) throw new IllegalArgumentException("c2j must be not null"); + } + + // called by subclasses + protected void init() { + importContext = new ImportContextImpl(getPackageName()); + + MetaAttribute metaAttribute = meta.getMetaAttribute("extra-import"); + if(metaAttribute!=null) { + for (String element : metaAttribute.getValues()) { + importContext.importType(element); + } + } + } + + protected String getPackageDeclaration(String pkgName) { + if (pkgName!=null && !pkgName.trim().isEmpty()) { + return "package " + pkgName + ";"; + } + else { + return "// default package"; + } + } + + public String getPackageDeclaration() { + String pkgName = getPackageName(); + return getPackageDeclaration(pkgName); + } + + /** Return package name. Note: Does not handle inner classes */ + public String getPackageName() { + String generatedClass = getGeneratedClassName(); + return StringHelper.qualifier(generatedClass.trim()); + } + + public String getShortName() { + return qualifyInnerClass(StringHelper.unqualify(getMappedClassName())); + } + + public String getQualifiedDeclarationName() { + String generatedName = qualifyInnerClass(getGeneratedClassName()); + String qualifier = StringHelper.qualifier( getMappedClassName() ); + if (!qualifier.isEmpty()) { + return qualifier + "." + generatedName; + } + else { + return generatedName; + } + } + + /** + * @return unqualified classname for this class (can be changed by meta attribute "generated-class") + */ + public String getDeclarationName() { + return qualifyInnerClass(StringHelper.unqualify( getGeneratedClassName() )); + } + + protected String getGeneratedClassName() + { + String generatedClass = getMetaAsString(MetaAttributeConstants.GENERATED_CLASS).trim(); + if(StringHelper.isEmpty(generatedClass) ) { + generatedClass = getMappedClassName(); + } + // will occur for + return Objects.requireNonNullElse(generatedClass, ""); + } + + protected String qualifyInnerClass(String className) + { + return className.replace('$', '.'); + } + + protected abstract String getMappedClassName(); + + public String getMetaAsString(String attribute) { + MetaAttribute c = meta.getMetaAttribute( attribute ); + return MetaAttributeHelper.getMetaAsString( c ); + } + + public boolean hasMetaAttribute(String attribute) { + return meta.getMetaAttribute( attribute ) != null; + } + + public String getMetaAsString(String attribute, String seperator) { + return MetaAttributeHelper.getMetaAsString( meta.getMetaAttribute( attribute ), seperator ); + } + + public boolean getMetaAsBool(String attribute) { + return getMetaAsBool( attribute, false ); + } + + public boolean getMetaAsBool(String attribute, boolean defaultValue) { + return MetaAttributeHelper.getMetaAsBool( meta.getMetaAttribute( attribute ), defaultValue ); + } + + public String getClassJavaDoc(String fallback, int indent) { + MetaAttribute c = meta.getMetaAttribute( CLASS_DESCRIPTION ); + if ( c == null ) { + return c2j.toJavaDoc( fallback, indent ); + } + else { + return c2j.toJavaDoc( getMetaAsString( CLASS_DESCRIPTION ), indent ); + } + } + + public String getClassModifiers() { + String classModifiers = null; + + // Get scope (backwards compatibility) + if ( meta.getMetaAttribute( SCOPE_CLASS ) != null ) { + classModifiers = getMetaAsString( SCOPE_CLASS ).trim(); + } + + // Get modifiers + if ( meta.getMetaAttribute( CLASS_MODIFIER ) != null ) { + classModifiers = getMetaAsString( CLASS_MODIFIER ).trim(); + } + return classModifiers == null ? "public" : classModifiers; + } + + public String getDeclarationType() { + boolean isInterface = isInterface(); + if ( isInterface ) { + return INTERFACE; + } + else { + return "class"; + } + } + + public boolean isInterface() { + return getMetaAsBool( INTERFACE ); + } + + public String getExtendsDeclaration() { + String extendz = getExtends(); + if ( extendz == null || extendz.trim().isEmpty()) { + return ""; + } + else { + return "extends " + extendz; + } + } + + public String getImplementsDeclaration() { + String implementz = getImplements(); + if ( implementz == null || implementz.trim().isEmpty()) { + return ""; + } + else { + return "implements " + implementz; + } + } + + public String generateEquals(String thisName, String otherName, boolean useGenerics) { + Iterator allPropertiesIterator = getEqualsHashCodePropertiesIterator(); + return generateEquals( thisName, otherName, allPropertiesIterator, useGenerics ); + } + + /** returns the properties that would be visible on this entity as a pojo. This does not return *all* properties since hibernate has certain properties that are only relevant in context of persistence. */ + public abstract Iterator getAllPropertiesIterator(); + + protected String generateEquals(String thisName, String otherName, Iterator allPropertiesIterator, boolean useGenerics) { + StringBuilder buf = new StringBuilder(); + while ( allPropertiesIterator.hasNext() ) { + Property property = (Property) allPropertiesIterator.next(); + if (!buf.isEmpty()) buf.append( "\n && " ); + String javaTypeName = c2j.getJavaTypeName( property, useGenerics, this ); + buf.append( + internalgenerateEquals( + javaTypeName, thisName + "." + getGetterSignature( property ) + "()", + otherName + "." + getGetterSignature( property ) + "()") + ); + } + + if (buf.isEmpty()) { + return "false"; + } + else { + return buf.toString(); + } + } + + private boolean usePropertyInEquals(Property property) { + boolean hasEqualsMetaAttribute = c2j.hasMetaAttribute(property, "use-in-equals"); + boolean useInEquals = c2j.getMetaAsBool( property, "use-in-equals" ); + + if(property.isNaturalIdentifier()) { + return !hasEqualsMetaAttribute || useInEquals; + } + + return useInEquals; + } + + private boolean useCompareTo(String javaTypeName) { + // Fix for HBX-400 + return "java.math.BigDecimal".equals(javaTypeName); + } + + + private String internalgenerateEquals(String typeName, String lh, String rh) { + if ( c2j.isPrimitive( typeName ) ) { + return "(" + lh + "==" + rh + ")"; + } + else { + if(useCompareTo( typeName )) { + return "( (" + lh + "==" + rh + ") || ( " + lh + "!=null && " + rh + "!=null && " + lh + ".compareTo(" + rh + ")==0 ) )"; + } else { + if(typeName.endsWith("[]")) { + return "( (" + lh + "==" + rh + ") || ( " + lh + "!=null && " + rh + "!=null && " + importType("java.util.Arrays") + ".equals(" + lh + ", " + rh + ") ) )"; + } else { + return "( (" + lh + "==" + rh + ") || ( " + lh + "!=null && " + rh + "!=null && " + lh + ".equals(" + rh + ") ) )"; + } + } + + } + } + + public String getExtraClassCode() { + return getMetaAsString( "class-code", "\n" ); + } + + private boolean needsEqualsHashCode(Iterator iter) { + while ( iter.hasNext() ) { + Property element = (Property) iter.next(); + if ( usePropertyInEquals( element ) ) { + return true; + } + } + return false; + } + + public boolean needsEqualsHashCode() { + Iterator iter = getAllPropertiesIterator(); + return needsEqualsHashCode( iter ); + } + + public abstract String getExtends(); + + public abstract String getImplements(); + + + public String importType(String fqcn) { + return importContext.importType(fqcn); + } + + public String generateImports() { + return importContext.generateImports(); + } + + public String staticImport(String fqcn, String member) { + return importContext.staticImport(fqcn, member); + } + + public String generateBasicAnnotation(Property property) { + StringBuffer annotations = new StringBuffer( " " ); + if(property.getValue() instanceof SimpleValue) { + if (hasVersionProperty()) + if (property.equals(getVersionProperty())) + buildVersionAnnotation(annotations); + String typeName = ((SimpleValue)property.getValue()).getTypeName(); + if("date".equals(typeName) || "java.sql.Date".equals(typeName)) { + buildTemporalAnnotation( annotations, "DATE" ); + } else if ("timestamp".equals(typeName) || "java.sql.Timestamp".equals(typeName)) { + buildTemporalAnnotation( annotations, "TIMESTAMP" ); + } else if ("time".equals(typeName) || "java.sql.Time".equals(typeName)) { + buildTemporalAnnotation(annotations, "TIME"); + } //TODO: calendar etc. ? + + + } + + return annotations.toString(); + } + + private void buildTemporalAnnotation(StringBuffer annotations, String temporalTypeValue) { + annotations + .append("@") + .append(importType("jakarta.persistence.Temporal")) + .append("(") + .append(importType("jakarta.persistence.TemporalType")) + .append(".") + .append(temporalTypeValue) + .append(")"); + } + + private void buildVersionAnnotation(StringBuffer annotations) { + annotations + .append("@") + .append(importType("jakarta.persistence.Version")); + } + + public String generateAnnColumnAnnotation(Property property) { + StringBuffer annotations = new StringBuffer( " " ); + boolean insertable = property.isInsertable(); + boolean updatable = property.isUpdatable(); + if ( property.isComposite() ) { + annotations.append("@").append(importType("jakarta.persistence.AttributeOverrides")).append("( {"); + Component component = (Component) property.getValue(); + Iterator subElements = component.getProperties().iterator(); + buildRecursiveAttributeOverride( subElements, null, annotations ); + annotations.setLength( annotations.length() - 2 ); + annotations.append( " } )" ); + } + else { + if ( property.getColumnSpan() == 1 ) { + Selectable selectable = property.getColumns().get(0); + buildColumnAnnotation( selectable, annotations, insertable, updatable ); + } + else { + annotations.append("@").append( importType("org.hibernate.annotations.Columns") ).append("( { " ); + for (Selectable selectable : property.getColumns()) { + //TODO formula in multicolumns not supported by annotations + if ( !selectable.isFormula() ) { + annotations.append( "\n " ); + buildColumnAnnotation( selectable, annotations, insertable, updatable ); + annotations.append( ", " ); + } + } + annotations.setLength( annotations.length() - 2 ); + annotations.append( " } )" ); + } + } + return annotations.toString(); + } + + private void buildRecursiveAttributeOverride(Iterator subElements, String path, StringBuffer annotations) { + while ( subElements.hasNext() ) { + Property subProperty = (Property) subElements.next(); + if ( subProperty.isComposite() ) { + if ( path != null ) { + path = path + "."; + } + else { + path = ""; + } + path = path + subProperty.getName(); + Component component = (Component) subProperty.getValue(); + buildRecursiveAttributeOverride( component.getProperties().iterator(), path, annotations ); + } + else { + Selectable selectable = subProperty.getColumns().get(0); + //TODO formula in multicolumns not supported by annotations + if ( !selectable.isFormula() ) { + annotations.append( "\n " ).append("@") + .append( importType("jakarta.persistence.AttributeOverride") ).append("(name=\"" ); + if ( path != null ) { + annotations.append( path ).append( "." ); + } + annotations.append( subProperty.getName() ).append( "\"" ) + .append( ", column=" ); + buildColumnAnnotation( + selectable, annotations, subProperty.isInsertable(), subProperty.isUpdatable() + ); + annotations.append( " ), " ); + } + } + } + } + + private void buildColumnAnnotation(Selectable selectable, StringBuffer annotations, boolean insertable, boolean updatable) { + if ( selectable.isFormula() ) { + annotations + .append("@") + .append( importType("org.hibernate.annotations.Formula") ) + .append("(value=\"" ) + .append( selectable.getText() ) + .append( "\")" ); + } + else { + Column column = (Column) selectable; + annotations + .append("@") + .append(importType("jakarta.persistence.Column")) + .append("(name=\"") + .append( column.getName() ) + .append( "\"" ); + appendCommonColumnInfo( annotations, column, insertable, updatable ); + + if (column.getPrecision() != null) { + annotations.append( ", precision=" ).append( column.getPrecision() ); + } + if (column.getScale() != null) { + annotations.append( ", scale=" ).append( column.getScale() ); + } + else if (column.getLength() != null){ + annotations.append( ", length=" ).append( column.getLength() ); + } + + + + + //TODO support secondary table + annotations.append( ")" ); + } + } + + protected void appendCommonColumnInfo(StringBuffer annotations, Column column, boolean insertable, boolean updatable) { + if(column.isUnique()) { + annotations.append( ", unique=" ).append( column.isUnique() ); + } + if(!column.isNullable()) { + annotations.append( ", nullable=" ).append( column.isNullable() ); + } + + if(!insertable) { + annotations.append( ", insertable=" ).append( insertable ); + } + + if(!updatable) { + annotations.append( ", updatable=" ).append( updatable ); + } + + String sqlType = column.getSqlType(); + if ( StringHelper.isNotEmpty( sqlType ) ) { + annotations.append( ", columnDefinition=\"" ).append( sqlType ).append( "\"" ); + } + + } + + + public Iterator getToStringPropertiesIterator() { + Iterator iter = getAllPropertiesIterator(); + return getToStringPropertiesIterator( iter ); + } + + private Iterator getToStringPropertiesIterator(Iterator iter) { + List properties = new ArrayList(); + + while ( iter.hasNext() ) { + Property element = (Property) iter.next(); + if ( c2j.getMetaAsBool( element, "use-in-tostring" ) ) { + properties.add( element ); + } + } + + return properties.iterator(); + } + + public Iterator getEqualsHashCodePropertiesIterator() { + Iterator iter = getAllPropertiesIterator(); + return getEqualsHashCodePropertiesIterator(iter); + } + + private Iterator getEqualsHashCodePropertiesIterator(Iterator iter) { + List properties = new ArrayList(); + + while ( iter.hasNext() ) { + Property element = (Property) iter.next(); + if ( usePropertyInEquals(element) ) { + properties.add( element ); + } + } + + return properties.iterator(); + } + + public boolean needsToString() { + Iterator iter = getAllPropertiesIterator(); + return needsToString( iter ); + } + + private boolean needsToString(Iterator iter) { + while ( iter.hasNext() ) { + Property element = (Property) iter.next(); + if ( c2j.getMetaAsBool( element, "use-in-tostring" ) ) { + return true; + } + } + return false; + } + + public boolean hasMetaAttribute(MetaAttributable pc, String attribute) { + return pc.getMetaAttribute( attribute ) != null; + } + + public boolean getMetaAttribAsBool(MetaAttributable pc, String attribute, boolean defaultValue) { + return MetaAttributeHelper.getMetaAsBool( pc.getMetaAttribute( attribute ), defaultValue ); + } + + public boolean hasFieldJavaDoc(Property property) { + return property.getMetaAttribute("field-description")!=null; + } + + public String getFieldJavaDoc(Property property, int indent) { + MetaAttribute c = property.getMetaAttribute( "field-description" ); + if ( c == null ) { + return c2j.toJavaDoc( "", indent ); + } + else { + return c2j.toJavaDoc( c2j.getMetaAsString( property, "field-description" ), indent ); + } + } + + public String getFieldDescription(Property property){ + MetaAttribute c = property.getMetaAttribute( "field-description" ); + if ( c == null ) { + return ""; + } + else { + return c2j.getMetaAsString( property, "field-description" ); + } + } + + /** + * Method getGetterSignature. + * + * @return String + */ + public String getGetterSignature(Property p) { + String prefix = c2j.getJavaTypeName( p, false).equals( "boolean" ) ? "is" : "get"; + return prefix + beanCapitalize( p.getName() ); + } + + /** + * @return foo -> Foo, FOo -> FOo + */ + public String getPropertyName(Property p) { + return beanCapitalize( p.getName() ); + } + + + // get the "opposite" collectionnae for a property. Currently a "hack" that just uses the same naming algorithm as in reveng, will fail on more general models! + public String getCollectionNameFor(Property property) { + String str = getPropertyName(property); + return NameConverter.simplePluralize(str); + } + + + /** + * foo -> Foo + * FOo -> FOo + */ + static public String beanCapitalize(String fieldname) { + if ( fieldname == null || fieldname.isEmpty()) { + return fieldname; + } + + if ( fieldname.length() > 1 && Character.isUpperCase( fieldname.charAt( 1 ) ) ) { + return fieldname; + } + char[] chars = fieldname.toCharArray(); + chars[0] = Character.toUpperCase( chars[0] ); + return new String( chars ); + } + + + public boolean isComponent(Property property) { + return property.getValue() instanceof Component; + } + + public String generateHashCode(Property property, String result, String thisName, boolean jdk5) { + StringBuilder buf = new StringBuilder(); + if ( c2j.getMetaAsBool( property, "use-in-equals" ) ) { + String javaTypeName = c2j.getJavaTypeName( property, jdk5, this ); + boolean isPrimitive = c2j.isPrimitive( javaTypeName ); + if ( isPrimitive ) { + buf.append( result ) + .append( " = 37 * " ) + .append( result ) + .append( " + " ); + String thisValue = thisName + "." + getGetterSignature( property ) + "()"; + if("char".equals(javaTypeName)||"int".equals(javaTypeName)||"short".equals(javaTypeName)||"byte".equals(javaTypeName)) { + buf.append( thisValue ); + } else if("boolean".equals(javaTypeName)) { + buf.append("(").append(thisValue).append("?1:0)"); + } else { + buf.append( "(int) "); + buf.append( thisValue ); + } + buf.append(";"); + } + else { + if(javaTypeName.endsWith("[]")) { + if(jdk5) { + buf.append( result ) + .append( " = 37 * " ) + .append( result ) + .append( " + " ) + .append("( ") + .append(getGetterSignature(property)) + .append("() == null ? 0 : ") + .append(importType("java.util.Arrays")) + .append(".hashCode(") + .append( thisName ) + .append( "." ) + .append( getGetterSignature( property ) ) + .append( "())" ) + .append( " )" ) + .append(";"); + } + else { + buf.append(internalGenerateArrayHashcode(property, javaTypeName, result, thisName)); + } + } else { + buf.append( result ) + .append( " = 37 * " ) + .append( result ) + .append( " + " ); + buf.append( "( " ) + .append( getGetterSignature( property ) ) + .append( "() == null ? 0 : " ) + .append( thisName ) + .append( "." ) + .append( getGetterSignature( property ) ) + .append( "()" ) + .append( ".hashCode()" ) + .append( " )" ) + .append(";"); + } + } + } + return buf.toString(); + } + + + private String internalGenerateArrayHashcode(Property property, String javaTypeName, String result, String thisName) + { + StringBuilder buf = new StringBuilder(); + + String propertyHashVarName = property.getName() + "Hashcode"; + String propertyArrayName = property.getName() + "Property"; + +// int propertyHash = 0; + buf.append( "int ") + .append( propertyHashVarName ) + .append( " = 0;\n" ); + +// type[] proterty = getProperty(); + buf.append( " " ) + .append( javaTypeName ) + .append( " " ) + .append( propertyArrayName ) + .append( " = " ) + .append( thisName ) + .append( "." ) + .append( getGetterSignature( property ) ) + .append( "();\n"); + +// if(property != null) { + buf.append( " if(" ) + .append( propertyArrayName ) + .append( " != null) {\n" ); + +// propertyHash = 1; + buf.append( " " ) + .append( propertyHashVarName ) + .append( " = 1;\n" ); + +// for (int i=0; i>> 32)); + buf.append( " int elementHash = (int)(" ) + .append( propertyArrayName ) + .append( "[i] ^ (" ) + .append( propertyArrayName ) + .append( "[i] >>> 32));\n" ); + +// propertyHash = 37 * propertyHash + elementHash; + buf.append( " " ) + .append( propertyHashVarName ) + .append( " = 37 * " ) + .append( propertyHashVarName ) + .append( " + elementHash;\n" ); + } else if(javaTypeName.startsWith("boolean")) { +// propertyHash = 37 * propertyHash + (propertyArray[i] ? 1231 : 1237); + buf.append( " " ) + .append( propertyHashVarName ) + .append( " = 37 * " ) + .append( propertyHashVarName ) + .append( " + (" ) + .append( propertyArrayName ) + .append( "[i] ? 1231 : 1237);\n" ); + } else if(javaTypeName.startsWith("float")) { +// propertyHash = 37 * propertyHash + Float.floatToIntBits(propertyArray[i]); + buf.append( " " ) + .append( propertyHashVarName ) + .append( " = 37 * " ) + .append( propertyHashVarName ) + .append( " + Float.floatToIntBits(" ) + .append( propertyArrayName ) + .append( "[i]);\n" ); + } else if(javaTypeName.startsWith("double")) { +// long bits = Double.doubleToLongBits(propertyArray[i]); + buf.append( " long bits = Double.doubleToLongBits(" ) + .append( propertyArrayName ) + .append( "[i]);\n" ); + +// propertyHash = 37 * propertyHash + (int)(bits ^ (bits >>> 32)); + buf.append( " " ) + .append( propertyHashVarName ) + .append( " = 37 * " ) + .append( propertyHashVarName ) + .append( " + (int)(bits ^ (bits >>> 32));\n" ); + } else if(javaTypeName.startsWith("int") + || javaTypeName.startsWith("short") + || javaTypeName.startsWith("char") + || javaTypeName.startsWith("byte")) { +// propertyHash = 37 * propertyHash + propertyArray[i]; + buf.append( " " ) + .append( propertyHashVarName ) + .append( " = 37 * " ) + .append( propertyHashVarName ) + .append( " + " ) + .append( propertyArrayName ) + .append( "[i];\n" ); + } else {// Object[] +// propertyHash = 37 * propertyHash + propertyArray[i].hashCode(); + buf.append( " " ) + .append( propertyHashVarName ) + .append( " = 37 * " ) + .append( propertyHashVarName ) + .append( " + " ) + .append( propertyArrayName ) + .append( "[i].hashCode();\n" ); + } + + buf.append( " }\n" ); + buf.append( " }\n\n" ); + +// result = 37 * result + arrayHashcode; + buf.append( " " ) + .append( result ) + .append( " = 37 * " ) + .append( result ) + .append( " + " ) + .append( propertyHashVarName ) + .append( ";\n" ); + + return buf.toString(); + } + + + public String getFieldModifiers(Property property) { + return getModifiers( property, "scope-field", "private" ); + } + + public String getPropertyGetModifiers(Property property) { + return getModifiers( property, "scope-get", "public" ); + } + + public String getPropertySetModifiers(Property property) { + return getModifiers( property, "scope-set", "public" ); + } + + //TODO defaultModifiers + private String getModifiers(Property property, String modifiername, String defaultModifiers) { + MetaAttribute override = property.getMetaAttribute( modifiername ); + if ( override != null ) { + return MetaAttributeHelper.getMetaAsString( override ); + } + else { + return defaultModifiers; + } + } + + protected boolean isRequiredInConstructor(Property field) { + if(hasMetaAttribute(field, "default-value")) { + return false; + } + if(field.getValue()!=null) { + if (!(field.isOptional() || field.getValue().isNullable()) && (field.getValueGeneratorCreator() == null )) { + return true; + } else if (field.getValue() instanceof Component c) { + for (Property prop : c.getProperties()) { + if (isRequiredInConstructor(prop)) { + return true; + } + } + } + } + + return false; + } + + public boolean needsMinimalConstructor() { + List propClosure = getPropertyClosureForMinimalConstructor(); + if(propClosure.isEmpty()) return false; // minimal=default + return !propClosure.equals(getPropertyClosureForFullConstructor()); // minimal=full + } + + public boolean needsFullConstructor() { + return !getPropertyClosureForFullConstructor().isEmpty(); + } + + public String getJavaTypeName(Property p, boolean useGenerics) { + return c2j.getJavaTypeName(p, useGenerics, this); + } + + static private class DefaultInitializor { + + private final String type; + private final boolean initToZero; + + public DefaultInitializor(String type, boolean initToZero) { + this.type = type; + this.initToZero = initToZero; + } + + public String getDefaultValue(String comparator, String genericDeclaration, ImportContext importContext) { + StringBuilder val = new StringBuilder("new " + importContext.importType(type)); + if(genericDeclaration!=null) { + val.append(genericDeclaration); + } + + val.append("("); + if(comparator!=null) { + val.append("new "); + val.append(importContext.importType(comparator)); + val.append("()"); + if(initToZero) val.append(","); + } + if(initToZero) { + val.append("0"); + } + val.append(")"); + return val.toString(); + } + + } + + static Map defaultInitializors = new HashMap(); + static { + defaultInitializors.put("java.util.List", new DefaultInitializor("java.util.ArrayList", true)); + defaultInitializors.put("java.util.Map", new DefaultInitializor("java.util.HashMap", true)); + defaultInitializors.put("java.util.Set", new DefaultInitializor("java.util.HashSet",true)); + defaultInitializors.put("java.util.SortedSet", new DefaultInitializor("java.util.TreeSet", false)); + defaultInitializors.put("java.util.SortedMap", new DefaultInitializor("java.util.TreeMap", false)); + } + + public boolean hasFieldInitializor(Property p, boolean useGenerics) { + return getFieldInitialization(p, useGenerics)!=null; + } + + public String getFieldInitialization(Property p, boolean useGenerics) { + if(hasMetaAttribute(p, "default-value")) { + return MetaAttributeHelper.getMetaAsString( p.getMetaAttribute( "default-value" ) ); + } + if(c2j.getJavaTypeName(p, false)==null) { + throw new IllegalArgumentException(); + } else if (p.getValue() instanceof Collection col) { + + DefaultInitializor initialization = (DefaultInitializor) col.accept(new DefaultValueVisitor(true) { + + public Object accept(Bag o) { + return new DefaultInitializor("java.util.ArrayList", true); + } + + public Object accept(org.hibernate.mapping.List o) { + return new DefaultInitializor("java.util.ArrayList", true); + } + + public Object accept(org.hibernate.mapping.Map o) { + if(o.isSorted()) { + return new DefaultInitializor("java.util.TreeMap", false); + } else { + return new DefaultInitializor("java.util.HashMap", true); + } + } + + public Object accept(IdentifierBag o) { + return new DefaultInitializor("java.util.ArrayList", true); + } + + public Object accept(Set o) { + if(o.isSorted()) { + return new DefaultInitializor("java.util.TreeSet", false); + } else { + return new DefaultInitializor("java.util.HashSet", true); + } + } + + + public Object accept(PrimitiveArray o) { + return null; // TODO: default init for arrays ? + } + + public Object accept(Array o) { + return null;// TODO: default init for arrays ? + } + + }); + + if(initialization!=null) { + String comparator = null; + String decl = null; + + if(col.isSorted()) { + comparator = col.getComparatorClassName(); + } + + if(useGenerics) { + decl = c2j.getGenericCollectionDeclaration((Collection) p.getValue(), importContext); + } + return initialization.getDefaultValue(comparator, decl, this); + } else { + return null; + } + } else { + return null; + } + } + +} + diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/Cfg2JavaTool.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/Cfg2JavaTool.java new file mode 100644 index 000000000000..bb5eff8ad8d1 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/Cfg2JavaTool.java @@ -0,0 +1,416 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +import org.hibernate.boot.internal.NamedHqlQueryDefinitionImpl; +import org.hibernate.boot.query.NamedHqlQueryDefinition; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.Array; +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.IndexedCollection; +import org.hibernate.mapping.MetaAttributable; +import org.hibernate.mapping.MetaAttribute; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.mapping.Value; +import org.hibernate.tool.reveng.internal.export.hbm.Cfg2HbmTool; +import org.hibernate.tool.reveng.internal.core.util.EnhancedValue; +import org.hibernate.tool.reveng.internal.util.NameConverter; +import org.hibernate.tool.reveng.internal.util.StringUtil; +import org.hibernate.type.Type; +import org.hibernate.type.spi.TypeConfiguration; +import org.jboss.logging.Logger; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +/** + * Helper methods for javacode generation. + *

+ * + * + * @author max + */ +public class Cfg2JavaTool { + + private static final Logger log = Logger.getLogger( Cfg2JavaTool.class ); + + public Cfg2JavaTool() { + + } + + public POJOClass getPOJOClass(Component comp) { + return new ComponentPOJOClass(comp, this); + } + + public POJOClass getPOJOClass(PersistentClass comp) { + return new EntityPOJOClass(comp, this); + } + + public String unqualify(String name) { + return StringHelper.unqualify( name ); + } + + /** + * Returns all meta items as one large string. + * + */ + public String getMetaAsString(MetaAttributable pc, String attribute) { + MetaAttribute c = pc.getMetaAttribute( attribute ); + + return MetaAttributeHelper.getMetaAsString( c ); + } + + public boolean hasMetaAttribute(MetaAttributable pc, String attribute) { + return pc.getMetaAttribute( attribute ) != null; + } + + public String getMetaAsString(MetaAttributable pc, String attribute, String seperator) { + return MetaAttributeHelper.getMetaAsString( pc.getMetaAttribute( attribute ), seperator ); + } + + public boolean getMetaAsBool(MetaAttributable ma, String attribute) { + return getMetaAsBool( ma, attribute, false ); + } + + public boolean getMetaAsBool(MetaAttributable pc, String attribute, boolean defaultValue) { + return MetaAttributeHelper.getMetaAsBool( pc.getMetaAttribute( attribute ), defaultValue ); + } + + + + /** + * Convert string into something that can be rendered nicely into a javadoc + * comment. + * Prefix each line with a star ('*'). + */ + public String toJavaDoc(String string, int indent) { + StringBuilder result = new StringBuilder(); + + if ( string != null ) { + String[] lines = StringUtil.split( string, "\n\r\f" ); + for ( int i = 0; i < lines.length ; i++ ) { + String docline = " * " + lines[i]; + if ( i < lines.length - 1 ) docline += "\n"; + result.append( StringUtil.leftPad( docline, docline.length() + indent ) ); + } + } + + return result.toString(); + } + + public String getClassModifiers(MetaAttributable pc) { + String classModifiers = null; + + // Get scope (backwards compatibility) + if ( pc.getMetaAttribute( "scope-class" ) != null ) { + classModifiers = getMetaAsString( pc, "scope-class" ).trim(); + } + + // Get modifiers + if ( pc.getMetaAttribute( "class-modifier" ) != null ) { + classModifiers = getMetaAsString( pc, "class-modifier" ).trim(); + } + return classModifiers == null ? "public" : classModifiers; + } + + + private String toName(Class c) { + + if ( c.isArray() ) { + Class a = c.getComponentType(); + + return a.getName() + "[]"; + } + else { + return c.getName(); + } + } + + /** + * Returns the typename for a property, using generics if this is a Set type and useGenerics is set to true. + */ + public String getJavaTypeName(Property p, boolean useGenerics) { + return getJavaTypeName(p, useGenerics, new NoopImportContext()); + } + + public String getJavaTypeName(Property p, boolean useGenerics, ImportContext importContext) { + String overrideType = getMetaAsString( p, "property-type" ); + if ( !StringHelper.isEmpty( overrideType ) ) { + String importType = importContext.importType(overrideType); + if ( useGenerics && !importType.contains("<")) { + if ( p.getValue() instanceof Collection ) { + String decl = getGenericCollectionDeclaration( (Collection) p.getValue(), importContext ); + return importType + decl; + } + } + return importType; + } + else { + String rawType = getRawTypeName( p, useGenerics, importContext ); + if(rawType==null) { + throw new IllegalStateException("getJavaTypeName *must* return a value"); + } + return importContext.importType(rawType); + } + } + + private static final Map PRIMITIVES = + new HashMap(); + + static { + PRIMITIVES.put( "char", "Character" ); + PRIMITIVES.put( "byte", "Byte" ); + PRIMITIVES.put( "short", "Short" ); + PRIMITIVES.put( "int", "Integer" ); + PRIMITIVES.put( "long", "Long" ); + PRIMITIVES.put( "boolean", "Boolean" ); + PRIMITIVES.put( "float", "Float" ); + PRIMITIVES.put( "double", "Double" ); + } + + static public boolean isNonPrimitiveTypeName(String typeName) { + return (!PRIMITIVES.containsKey( typeName )) + && new TypeConfiguration() + .getBasicTypeRegistry() + .getRegisteredType(typeName) != null; + } + + private String getRawTypeName(Property p, boolean useGenerics, ImportContext importContext) { + Value value = p.getValue(); + try { + + if (value instanceof Array a) { // array has a string rep.inside. + if ( a.isPrimitiveArray() ) { + return toName( value.getType().getReturnedClass() ); + } + else if (a.getElementClassName()!=null){ + return a.getElementClassName() + "[]"; + } else { + return getJavaTypeName(a.getElement()) + "[]"; + } + } + + if (value instanceof Component component) { // same for component. + if(component.isDynamic()) return "java.util.Map"; + return component.getComponentClassName(); + } + + if ( useGenerics ) { + if ( value instanceof Collection ) { + String decl = getGenericCollectionDeclaration( (Collection) value, importContext ); + return getJavaTypeName(value) + decl; + } + } + + return getJavaTypeName( value); + } + catch (Exception e) { + //e.printStackTrace(); + String msg = "Could not resolve type without exception for " + p + " Value: " + value; + if ( value != null && value.isSimpleValue() ) { + String typename = ( (SimpleValue) value ).getTypeName(); + log.warn( msg + ". Falling back to typename: " + typename ); + return typename; + } + else { + throw new RuntimeException( msg, e ); + } + } + } + + public String getGenericCollectionDeclaration(Collection collection, ImportContext importContext) { + Value element = collection.getElement(); + String elementType = importContext.importType(getJavaTypeName(element)); + String genericDecl = elementType; + if(collection.isIndexed()) { + IndexedCollection idxCol = (IndexedCollection) collection; + if(!idxCol.isList()) { + Value idxElement = idxCol.getIndex(); + String indexType = importContext.importType(getJavaTypeName(idxElement)); + genericDecl = indexType + "," + elementType; + } + } + return "<" + genericDecl + ">"; + } + + public Properties getFilteredIdentifierGeneratorProperties(SimpleValue simpleValue) { + Properties p = ((EnhancedValue)simpleValue).getIdentifierGeneratorProperties(); + return Cfg2HbmTool.getFilteredIdentifierGeneratorProperties(p, new Properties()); + } + + private String getJavaTypeName(Value value) { + return (String) value.accept( new JavaTypeFromValueVisitor() ); + } + + public String asParameterList(Iterator fields, boolean useGenerics, ImportContext ic) { + StringBuilder buf = new StringBuilder(); + while ( fields.hasNext() ) { + Property field = (Property)fields.next(); + buf.append( getJavaTypeName( field, useGenerics, ic ) ) + .append( " " ) + .append( keyWordCheck(field.getName()) ); + if ( fields.hasNext() ) { + buf.append( ", " ); + } + } + return buf.toString(); + } + + /** + * @param fields iterator on Property elements. + * @return "name, number, ..." for a property list, usable for method calls. + *

+ * TODO: handle this in a template ? + */ + public String asArgumentList(Iterator fields) { + StringBuilder buf = new StringBuilder(); + while ( fields.hasNext() ) { + Property field = (Property)fields.next(); + buf.append( keyWordCheck(field.getName()) ); + if ( fields.hasNext() ) { + buf.append( ", " ); + } + } + return buf.toString(); + } + + /** + * @param clazz persistent class. + * @return "String name, int number, ..." for a property list, usable for method declarations. + *

+ * TODO: handle this in a template ? + */ + public String asNaturalIdParameterList(PersistentClass clazz) { + StringBuilder buf = new StringBuilder(); + for (Property field : clazz.getRootClass().getProperties()) { + if ( field.isNaturalIdentifier() ) { + buf.append( getJavaTypeName( field, false ) ) + .append( " " ) + .append( field.getName() ) + .append( ", " ); + } + } + return buf.substring( 0, buf.length() - 2 ); + } + + public String asParameterList(List fields, boolean useGenerics, ImportContext ic) { + return asParameterList( fields.iterator(), useGenerics, ic ); + } + + public String asArgumentList(List fields) { + return asArgumentList( fields.iterator() ); + } + + public String asFinderArgumentList(Map parameterTypes, ImportContext ctx) { + StringBuilder buf = new StringBuilder(); + Iterator> iter = parameterTypes.entrySet().iterator(); + while ( iter.hasNext() ) { + Entry entry = iter.next(); + String typename = null; + Type type = null; + if(entry.getValue() instanceof String) { + try { + type = new TypeConfiguration() + .getBasicTypeRegistry() + .getRegisteredType((String) entry.getValue()); + } catch(Throwable t) { + typename = (String) entry.getValue(); + } + } + + if(type!=null) { + typename = type.getReturnedClass().getName(); + } + buf.append( ctx.importType( typename )) + .append( " " ) + .append( entry.getKey() ); + if ( iter.hasNext() ) buf.append( ", " ); + } + return buf.toString(); + } + + + + public boolean isPrimitive(String typeName) { + return PRIMITIVES.containsKey(typeName); + } + + public boolean isComponent(Property property) { + return isComponent(property.getValue()); + } + + public boolean isComponent(Value value) { + return ( value instanceof Component ); + } + + // TODO: should consult exporter/cfg2java tool for cached POJOEntities....or maybe not since they + // have their own state... + public Iterator getPOJOIterator( + final Iterator persistentClasses) { + return new Iterator() { + public POJOClass next() { + return getPOJOClass((PersistentClass)persistentClasses.next()); + } + public boolean hasNext() { + return persistentClasses.hasNext(); + } + public void remove() { + persistentClasses.remove(); + } + }; + } + + + public String simplePluralize(String str) { + return NameConverter.simplePluralize(str); + } + + public String keyWordCheck(String possibleKeyword) { + if(NameConverter.isReservedJavaKeyword(possibleKeyword)) { + possibleKeyword = possibleKeyword + "_"; + } + return possibleKeyword; + } + + public boolean isArray(String typeName) { + return typeName!=null && typeName.endsWith("[]"); + } + + public Map getParameterTypes(NamedHqlQueryDefinition query) { + Map result = null; + try { + Field field = NamedHqlQueryDefinitionImpl.class.getDeclaredField("parameterTypes"); + field.setAccessible(true); + result = (Map)field.get(query); + if (result == null) { + result = new HashMap<>(); + } + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + return result; + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ComponentPOJOClass.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ComponentPOJOClass.java new file mode 100644 index 000000000000..bc51ac441448 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ComponentPOJOClass.java @@ -0,0 +1,190 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +import org.hibernate.mapping.Component; +import org.hibernate.mapping.MetaAttribute; +import org.hibernate.mapping.Property; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import static org.hibernate.tool.reveng.internal.export.java.MetaAttributeConstants.EXTENDS; +import static org.hibernate.tool.reveng.internal.export.java.MetaAttributeConstants.IMPLEMENTS; + +public class ComponentPOJOClass extends BasicPOJOClass { + + private final Component clazz; + + public ComponentPOJOClass(Component component, Cfg2JavaTool cfg) { + super(component, cfg); + this.clazz = component; + init(); + } + + protected String getMappedClassName() { + return clazz.getComponentClassName(); + } + + public String getExtends() { + String extendz = ""; + + if ( isInterface() ) { + if ( clazz.getMetaAttribute( EXTENDS ) != null ) { + extendz += getMetaAsString( EXTENDS, "," ); + } + } + else if ( clazz.getMetaAttribute( EXTENDS ) != null ) { + extendz = getMetaAsString( EXTENDS, "," ); + } + + return extendz.isEmpty() ? null : extendz; + } + + public String getImplements() { + List interfaces = new ArrayList(); + + // implement proxy, but NOT if the proxy is the class it self! + // interfaces can't implement stuff + if ( !isInterface() ) { + MetaAttribute implementz = clazz.getMetaAttribute( IMPLEMENTS ); + if ( implementz != null ) { + interfaces.addAll(implementz.getValues()); + } + interfaces.add( Serializable.class.getName() ); // TODO: is this "nice" ? shouldn't it be a user choice ? + } + + + if (!interfaces.isEmpty()) { + StringBuilder sb = new StringBuilder(); + for ( Iterator iter = interfaces.iterator(); iter.hasNext() ; ) { + //sb.append(JavaTool.shortenType(iter.next().toString(), pc.getImports() ) ); + sb.append( iter.next() ); + if ( iter.hasNext() ) sb.append( "," ); + } + return sb.toString(); + } + else { + return null; + } + } + + public Iterator getAllPropertiesIterator() { + return clazz.getProperties().iterator(); + } + + public boolean isComponent() { + return true; + } + + public boolean hasIdentifierProperty() { + return false; + } + + public boolean needsAnnTableUniqueConstraints() { + return false; + } + + public String generateBasicAnnotation(Property property) { + return ""; + } + + public String generateAnnIdGenerator() { + return ""; + } + + public String generateAnnTableUniqueConstraint() { + return ""; + } + + public Object getDecoratedObject() { + return clazz; + } + + public boolean isSubclass() { + return false; + } + + public List getPropertiesForFullConstructor() { + List res = new ArrayList(); + + Iterator iter = getAllPropertiesIterator(); + while(iter.hasNext()) { + res.add(iter.next()); + } + return res; + } + + public List getPropertyClosureForFullConstructor() { + return getPropertiesForFullConstructor(); + } + + public List getPropertyClosureForSuperclassFullConstructor() { + return Collections.emptyList(); + } + + public List getPropertiesForMinimalConstructor() { + List res = new ArrayList(); + Iterator iter = getAllPropertiesIterator(); + while(iter.hasNext()) { + Property prop = (Property)iter.next(); + if(isRequiredInConstructor(prop)) { + res.add(prop); + } + } + return res; + } + + public List getPropertyClosureForMinimalConstructor() { + return getPropertiesForMinimalConstructor(); + } + + public List getPropertyClosureForSuperclassMinimalConstructor() { + return Collections.emptyList(); + } + + /* + * @see org.hibernate.tool.hbm2x.pojo.POJOClass#getSuperClass() + */ + public POJOClass getSuperClass() { + return null; + } + + public String toString() { + return "Component: " + (clazz==null?"":clazz.getComponentClassName()); + } + + public Property getIdentifierProperty(){ + return null; + } + + public boolean hasVersionProperty() { + return false; + } + + /* + * @see org.hibernate.tool.hbm2x.pojo.POJOClass#getVersionProperty() + */ + public Property getVersionProperty() + { + return null; + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/EntityPOJOClass.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/EntityPOJOClass.java new file mode 100644 index 000000000000..179e97e3634a --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/EntityPOJOClass.java @@ -0,0 +1,917 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +import org.hibernate.boot.Metadata; +import org.hibernate.id.PersistentIdentifierGenerator; +import org.hibernate.id.enhanced.TableGenerator; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.internal.util.collections.JoinedList; +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.Formula; +import org.hibernate.mapping.KeyValue; +import org.hibernate.mapping.ManyToOne; +import org.hibernate.mapping.OneToMany; +import org.hibernate.mapping.OneToOne; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.RootClass; +import org.hibernate.mapping.Selectable; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.mapping.Subclass; +import org.hibernate.mapping.Table; +import org.hibernate.mapping.ToOne; +import org.hibernate.mapping.UniqueKey; +import org.hibernate.mapping.Value; +import org.hibernate.tool.reveng.internal.core.util.EnhancedBasicValue; +import org.hibernate.tool.reveng.internal.core.util.EnhancedValue; +import org.hibernate.tool.reveng.internal.util.AnnotationBuilder; +import org.hibernate.tool.reveng.internal.util.IteratorTransformer; +import org.hibernate.tool.reveng.internal.util.SkipBackRefPropertyIterator; +import org.hibernate.tool.reveng.internal.util.ValueUtil; +import org.hibernate.type.ForeignKeyDirection; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.StringTokenizer; + +import static org.hibernate.tool.reveng.internal.export.java.MetaAttributeConstants.EXTENDS; +import static org.hibernate.tool.reveng.internal.export.java.MetaAttributeConstants.IMPLEMENTS; + +public class EntityPOJOClass extends BasicPOJOClass { + + private final PersistentClass clazz; + + public EntityPOJOClass(PersistentClass clazz, Cfg2JavaTool cfg) { + super(clazz, cfg); + this.clazz = clazz; + init(); + } + + protected String getMappedClassName() { + return clazz.getClassName(); + } + + /** + * @return whatever the class (or interface) extends (null if it does not extend anything) + */ + public String getExtends() { + String extendz = ""; + + if ( isInterface() ) { + if ( clazz.getSuperclass() != null ) { + extendz = clazz.getSuperclass().getClassName(); + } + if ( clazz.getMetaAttribute( EXTENDS ) != null ) { + if ( !extendz.isEmpty() ) { + extendz += ","; + } + extendz += getMetaAsString( EXTENDS, "," ); + } + } + else if ( clazz.getSuperclass() != null ) { + if (!( c2j.getPOJOClass(clazz.getSuperclass()).isInterface() )) { + extendz = clazz.getSuperclass().getClassName(); + } + } + else if ( clazz.getMetaAttribute( EXTENDS ) != null ) { + extendz = getMetaAsString( EXTENDS, "," ); + } + + return extendz.isEmpty() ? null : extendz; + } + + + public String getImplements() { + List interfaces = new ArrayList(); + + // implement proxy, but NOT if the proxy is the class it self! + if ( clazz.getProxyInterfaceName() != null && ( !clazz.getProxyInterfaceName().equals( clazz.getClassName() ) ) ) { + interfaces.add( clazz.getProxyInterfaceName() ); + } + + if ( !isInterface() ) { + if ( clazz.getSuperclass() != null && c2j.getPOJOClass(clazz.getSuperclass()).isInterface() ) { + interfaces.add( clazz.getSuperclass().getClassName() ); + } + if ( clazz.getMetaAttribute( IMPLEMENTS ) != null ) { + interfaces.addAll( clazz.getMetaAttribute( IMPLEMENTS ).getValues() ); + } + interfaces.add( Serializable.class.getName() ); // TODO: is this "nice" ? shouldn't it be a user choice ? + } + + if (!interfaces.isEmpty()) { + StringBuilder sbuf = new StringBuilder(); + for ( Iterator iter = interfaces.iterator(); iter.hasNext() ; ) { + //sbuf.append(JavaTool.shortenType(iter.next().toString(), pc.getImports() ) ); + sbuf.append( iter.next() ); + if ( iter.hasNext() ) sbuf.append( "," ); + } + return sbuf.toString(); + } + else { + return null; + } + } + + public Iterator getAllPropertiesIterator() { + return getAllPropertiesIterator(clazz); + } + + + public Iterator getAllPropertiesIterator(PersistentClass pc) { + List properties = new ArrayList(); + List> lists = new ArrayList>(); + if ( pc.getSuperclass() == null ) { + // only include identifier for the root class. + if ( pc.hasIdentifierProperty() ) { + properties.add( pc.getIdentifierProperty() ); + } + else if ( pc.hasEmbeddedIdentifier() ) { + Component embeddedComponent = (Component) pc.getIdentifier(); + lists.add( embeddedComponent.getProperties() ); + } + /*if(clazz.isVersioned() ) { // version is already in property set + properties.add(clazz.getVersion() ); + }*/ + } + + + // iterators.add( pc.getPropertyIterator() ); + // Need to skip element which are defined via "embedded" components + // Best if we could return an intelligent iterator, but for now we just iterate explicitly. + List pl = pc.getProperties(); + for(Property element : pl) + { + if ( element.getValue() instanceof Component component + && element.getPropertyAccessorName().equals( "embedded" )) { + properties.addAll(component.getProperties()); + } else { + properties.add(element); + } + } + + lists.add(properties); + + return new SkipBackRefPropertyIterator( new JoinedList( lists ).iterator() ); + } + + public boolean isComponent() { + return false; + } + + + public boolean hasIdentifierProperty() { + return clazz.hasIdentifierProperty() && clazz instanceof RootClass; + } + + public Property getIdentifierProperty() { + return clazz.getIdentifierProperty(); + } + + public String generateAnnTableUniqueConstraint() { + if ( ! ( clazz instanceof Subclass ) ) { + Table table = clazz.getTable(); + return generateAnnTableUniqueConstraint( table ); + } + return ""; + } + + protected String generateAnnTableUniqueConstraint(Table table) { + List cons = new ArrayList(); + for (UniqueKey key : table.getUniqueKeys().values()) { + if (table.hasPrimaryKey() && table.getPrimaryKey().getColumns().equals(key.getColumns())) { + continue; + } + AnnotationBuilder constraint = AnnotationBuilder.createAnnotation( importType("jakarta.persistence.UniqueConstraint") ); + constraint.addQuotedAttributes( "columnNames", new IteratorTransformer(key.getColumns().iterator()) { + public String transform(Column column) { + return column.getName(); + } + }); + cons.add( constraint.getResult() ); + } + + AnnotationBuilder builder = AnnotationBuilder.createAnnotation( "dummyAnnotation" ); + builder.addAttributes( "dummyAttribute", cons.iterator() ); + String attributeAsString = builder.getAttributeAsString( "dummyAttribute" ); + return attributeAsString==null?"":attributeAsString; + } + + + public String generateAnnIdGenerator() { + KeyValue identifier = clazz.getIdentifier(); + String strategy = null; + Properties properties = null; + StringBuffer wholeString = new StringBuffer( " " ); + if ( identifier instanceof Component ) { + + wholeString.append( AnnotationBuilder.createAnnotation( importType("jakarta.persistence.EmbeddedId") ).getResult()); + } + else if (identifier instanceof EnhancedBasicValue enhancedBasicValue) { + strategy = enhancedBasicValue.getIdentifierGeneratorStrategy(); + properties = c2j.getFilteredIdentifierGeneratorProperties(enhancedBasicValue); + StringBuilder idResult = new StringBuilder(); + AnnotationBuilder builder = AnnotationBuilder.createAnnotation( importType("jakarta.persistence.Id") ); + idResult.append(builder.getResult()); + idResult.append(" "); + + boolean isGenericGenerator = false; //TODO: how to handle generic now?? + if ( !"assigned".equals( strategy ) ) { + + if ( !"native".equals( strategy ) ) { + if ( "identity".equals( strategy ) ) { + builder.resetAnnotation( importType("jakarta.persistence.GeneratedValue") ); + builder.addAttribute( "strategy", staticImport("jakarta.persistence.GenerationType", "IDENTITY" ) ); + idResult.append(builder.getResult()); + } + else if ( "sequence".equals( strategy ) ) { + String sequenceGeneratorName = null; + if (properties != null) { + sequenceGeneratorName = properties.getProperty( + org.hibernate.id.enhanced.SequenceStyleGenerator.SEQUENCE_PARAM, null ); + } + builder.resetAnnotation( importType("jakarta.persistence.GeneratedValue") ) + .addAttribute( "strategy", staticImport("jakarta.persistence.GenerationType", "SEQUENCE" ) ) + .addQuotedAttribute( "generator", clazz.getClassName()+"IdGenerator" ); + idResult.append(builder.getResult()); + + builder.resetAnnotation( importType("jakarta.persistence.SequenceGenerator") ) + .addQuotedAttribute( "name", clazz.getClassName()+"IdGenerator" ) + .addQuotedAttribute( "sequenceName", sequenceGeneratorName ); + // TODO HA does not support initialValue and allocationSize + wholeString.append( builder.getResult() ); + } + else if ( TableGenerator.class.getName().equals( strategy ) ) { + builder.resetAnnotation( importType("jakarta.persistence.GeneratedValue") ) + .addAttribute( "strategy", staticImport("jakarta.persistence.GenerationType", "TABLE" ) ) + .addQuotedAttribute( "generator", clazz.getClassName()+"IdGenerator" ); + idResult.append(builder.getResult()); + buildAnnTableGenerator( wholeString, properties ); + } + else { + isGenericGenerator = true; + builder.resetAnnotation( importType("jakarta.persistence.GeneratedValue") ); + builder.addQuotedAttribute( "generator", clazz.getClassName()+"IdGenerator" ); + idResult.append(builder.getResult()); + } + } else { + builder.resetAnnotation( importType("jakarta.persistence.GeneratedValue") ); + idResult.append(builder.getResult()); + } + } + if ( isGenericGenerator ) { + builder.resetAnnotation( importType("org.hibernate.annotations.GenericGenerator") ) + .addQuotedAttribute( "name", clazz.getClassName()+"IdGenerator" ) + .addQuotedAttribute( "strategy", strategy); + + List params = new ArrayList(); + //wholeString.append( "parameters = { " ); + if ( properties != null ) { + Enumeration propNames = properties.propertyNames(); + while ( propNames.hasMoreElements() ) { + + String propertyName = (String) propNames.nextElement(); + AnnotationBuilder parameter = AnnotationBuilder.createAnnotation( importType("org.hibernate.annotations.Parameter") ) + .addQuotedAttribute( "name", propertyName ) + .addQuotedAttribute( "value", properties.getProperty( propertyName ) ); + params.add( parameter ); + } + } + builder.addAttributes( "parameters", params.iterator() ); + wholeString.append(builder.getResult()); + } + wholeString.append( idResult ); + } + return wholeString.toString(); + } + + private void buildAnnTableGenerator(StringBuffer wholeString, Properties properties) { + + AnnotationBuilder builder = AnnotationBuilder.createAnnotation( importType("jakarta.persistence.TableGenerator") ); + builder.addQuotedAttribute( "name", clazz.getClassName()+"IdGenerator" ); + builder.addQuotedAttribute( "table", properties.getProperty( "generatorTableName", "hibernate_sequences" ) ); + if (propertyDoesNotHaveDefaultValue( PersistentIdentifierGenerator.CATALOG, properties ) ) { + builder.addQuotedAttribute( "catalog", properties.getProperty( PersistentIdentifierGenerator.CATALOG, "") ); + } + if (propertyDoesNotHaveDefaultValue( PersistentIdentifierGenerator.SCHEMA, properties ) ) { + builder.addQuotedAttribute( "schema", properties.getProperty( PersistentIdentifierGenerator.SCHEMA, "") ); + } + if (propertyDoesNotHaveDefaultValue( TableGenerator.SEGMENT_VALUE_PARAM, properties ) ) { + builder.addQuotedAttribute( "pkColumnValue", properties.getProperty( TableGenerator.SEGMENT_VALUE_PARAM, "") ); + } + if (propertyDoesNotHaveDefaultValue( TableGenerator.INCREMENT_PARAM, properties, "50" ) ) { + builder.addAttribute( "allocationSize", properties.getProperty( TableGenerator.INCREMENT_PARAM, "50" ) ); + } + if (propertyDoesNotHaveDefaultValue( TableGenerator.SEGMENT_COLUMN_PARAM, properties ) ) { + builder.addQuotedAttribute( "pkColumnName", properties.getProperty( TableGenerator.SEGMENT_COLUMN_PARAM, "") ); + } + if (propertyDoesNotHaveDefaultValue( TableGenerator.VALUE_COLUMN_PARAM, properties) ) { + builder.addQuotedAttribute( "valueColumnName", properties.getProperty( TableGenerator.VALUE_COLUMN_PARAM, "") ); + } + wholeString.append(builder.getResult()).append("\n "); + } + + private boolean propertyDoesNotHaveDefaultValue(String property, Properties properties) { + return propertyDoesNotHaveDefaultValue(property, properties, null); + } + + private boolean propertyDoesNotHaveDefaultValue(String property, Properties properties, String defaultValue) { + String propertyValue = properties.getProperty(property); + if (defaultValue == null) { + return StringHelper.isNotEmpty(propertyValue); + } else { + return !defaultValue.equals(propertyValue); + } + } + + public String generateJoinColumnsAnnotation(Property property, Metadata md) { + boolean insertable = property.isInsertable(); + boolean updatable = property.isUpdatable(); + Value value = property.getValue(); + int span; + Iterator selectablesIterator; + Iterator referencedSelectablesIterator = null; + if (value instanceof Collection collection) { + span = collection.getKey().getColumnSpan(); + selectablesIterator = collection.getKey().getSelectables().iterator(); + } + else { + span = property.getColumnSpan(); + selectablesIterator = property.getSelectables().iterator(); + } + + if(property.getValue() instanceof ToOne) { + String referencedEntityName = ((ToOne)property.getValue()).getReferencedEntityName(); + PersistentClass target = md.getEntityBinding(referencedEntityName); + if(target!=null) { + referencedSelectablesIterator = target.getKey().getSelectables().iterator(); + } + } + + StringBuffer annotations = new StringBuffer( " " ); + if ( span == 1 ) { + Selectable selectable = selectablesIterator.next(); + buildJoinColumnAnnotation( selectable, null, annotations, insertable, updatable ); + } + else { + annotations.append("@").append( importType("jakarta.persistence.JoinColumns") ).append("( { " ); + buildArrayOfJoinColumnAnnotation(selectablesIterator, referencedSelectablesIterator, annotations, insertable, updatable ); + annotations.append( " } )" ); + } + return annotations.toString(); + } + + private void buildArrayOfJoinColumnAnnotation( + Iterator columns, + Iterator referencedColumnsIterator, + StringBuffer annotations, + boolean insertable, + boolean updatable + ) { + while ( columns.hasNext() ) { + Selectable selectable = columns.next(); + Selectable referencedColumn = null; + if(referencedColumnsIterator!=null) { + referencedColumn = referencedColumnsIterator.next(); + } + + //TODO formula in multicolumns not supported by annotations + if (!( selectable.isFormula() )) { + annotations.append( "\n " ); + buildJoinColumnAnnotation( selectable, referencedColumn, annotations, insertable, updatable ); + annotations.append( ", " ); + } + } + annotations.setLength( annotations.length() - 2 ); + } + + private void buildJoinColumnAnnotation( + Selectable selectable, + Selectable referencedColumn, + StringBuffer annotations, + boolean insertable, + boolean updatable) { + //TODO not supported by HA + if (!( selectable.isFormula() )) { + Column column = (Column) selectable; + annotations.append("@").append( importType("jakarta.persistence.JoinColumn") ) + .append("(name=\"" ).append( column.getName() ).append( "\"" ); + //TODO handle referenced column name, this is a hard one + if(referencedColumn!=null) { + annotations.append(", referencedColumnName=\"" ).append( referencedColumn.getText() ).append( "\"" ); + } + + appendCommonColumnInfo(annotations, column, insertable, updatable); + //TODO support secondary table + annotations.append( ")" ); + } + } + + public String[] getCascadeTypes(Property property) { + StringTokenizer st = new StringTokenizer( property.getCascade(), ", ", false ); + List types = new ArrayList(); + while ( st.hasMoreElements() ) { + String element = ( (String) st.nextElement() ).toLowerCase(); + switch (element) { + case "persist" -> types.add(importType("jakarta.persistence.CascadeType") + ".PERSIST"); + case "merge" -> types.add(importType("jakarta.persistence.CascadeType") + ".MERGE"); + case "delete" -> types.add(importType("jakarta.persistence.CascadeType") + ".REMOVE"); + case "refresh" -> types.add(importType("jakarta.persistence.CascadeType") + ".REFRESH"); + case "all" -> types.add(importType("jakarta.persistence.CascadeType") + ".ALL"); + } + } + return types.toArray(new String[0]); + } + + public String generateManyToOneAnnotation(Property property) { + return AnnotationBuilder.createAnnotation(importType("jakarta.persistence.ManyToOne")) + .addAttribute("cascade", getCascadeTypes(property)) + .addAttribute("fetch", getFetchType(property)) + .getResult() + getHibernateCascadeTypeAnnotation(property); + } + + public boolean isSharedPkBasedOneToOne(OneToOne oneToOne){ + Iterator joinSelectablesIt = oneToOne.getSelectables().iterator(); + Set joinSelectables = new HashSet(); + while ( joinSelectablesIt.hasNext() ) { + joinSelectables.add( joinSelectablesIt.next() ); + } + + if (joinSelectables.isEmpty()) + return false; + + for (Selectable selectable : getIdentifierProperty().getSelectables()) { + if (!joinSelectables.contains(selectable)) + return false; + } + + return true; + } + + public String generateOneToOneAnnotation(Property property, Metadata md) { + OneToOne oneToOne = (OneToOne)property.getValue(); + + boolean pkIsAlsoFk = isSharedPkBasedOneToOne(oneToOne); + + AnnotationBuilder ab = AnnotationBuilder.createAnnotation( importType("jakarta.persistence.OneToOne") ) + .addAttribute( "cascade", getCascadeTypes(property)) + .addAttribute( "fetch", getFetchType(property)); + + if ( oneToOne.getForeignKeyType().equals(ForeignKeyDirection.TO_PARENT) ){ + ab.addQuotedAttribute("mappedBy", getOneToOneMappedBy(md, oneToOne)); + } + + StringBuilder buffer = new StringBuilder(ab.getResult()); + buffer.append(getHibernateCascadeTypeAnnotation(property)); + + if ( pkIsAlsoFk && oneToOne.getForeignKeyType().equals(ForeignKeyDirection.FROM_PARENT) ){ + AnnotationBuilder ab1 = AnnotationBuilder.createAnnotation( importType("jakarta.persistence.PrimaryKeyJoinColumn") ); + buffer.append(ab1.getResult()); + } + + return buffer.toString(); + } + + public String getHibernateCascadeTypeAnnotation(Property property) { + StringTokenizer st = new StringTokenizer( property.getCascade(), ", ", false ); + String cascadeType = null; + StringBuilder cascade = new StringBuilder(); + while ( st.hasMoreElements() ) { + String element = ( (String) st.nextElement() ).toLowerCase(); + switch (element) { + case "all-delete-orphan" -> { + if (cascadeType == null) cascadeType = importType("org.hibernate.annotations.CascadeType"); + cascade.append(cascadeType).append(".ALL").append(", ") + .append(cascadeType).append(".DELETE_ORPHAN").append(", "); + } + case "delete-orphan" -> { + if (cascadeType == null) cascadeType = importType("org.hibernate.annotations.CascadeType"); + cascade.append(cascadeType).append(".DELETE_ORPHAN").append(", "); + } + case "save-update" -> { + if (cascadeType == null) cascadeType = importType("org.hibernate.annotations.CascadeType"); + cascade.append(cascadeType).append(".MERGE").append(", "); + } + case "replicate" -> { + if (cascadeType == null) cascadeType = importType("org.hibernate.annotations.CascadeType"); + cascade.append(cascadeType).append(".REPLICATE").append(", "); + } + case "lock" -> { + if (cascadeType == null) cascadeType = importType("org.hibernate.annotations.CascadeType"); + cascade.append(cascadeType).append(".LOCK").append(", "); + } + case "evict" -> { + if (cascadeType == null) cascadeType = importType("org.hibernate.annotations.CascadeType"); + cascade.append(cascadeType).append(".EVICT").append(", "); + } + } + } + if ( cascade.length() >= 2 ) { + String hibernateCascade = importType("org.hibernate.annotations.Cascade"); + cascade.insert(0, "@" + hibernateCascade + "( {"); + cascade.setLength( cascade.length() - 2 ); + cascade.append("} )"); + } + return cascade.toString(); + } + + public String getFetchType(Property property) { + Value value = property.getValue(); + String fetchType = importType( "jakarta.persistence.FetchType"); + boolean lazy = false; + if ( value instanceof ToOne ) { + lazy = ( (ToOne) value ).isLazy(); + } + else if ( value instanceof Collection ) { + lazy = ( (Collection) value ).isLazy(); + } + else { + //we're not collection neither *toone so we are looking for property fetching + lazy = property.isLazy(); + } + if ( lazy ) { + return fetchType + "." + "LAZY"; + } + else { + return fetchType + "." + "EAGER"; + } + } + + public Object getDecoratedObject() { + return clazz; + } + + public String generateCollectionAnnotation(Property property, Metadata md) { + StringBuffer annotation = new StringBuffer(); + Value value = property.getValue(); + if (value instanceof Collection collection) { + if ( collection.isOneToMany() ) { + String mappedBy = null; + AnnotationBuilder ab = AnnotationBuilder.createAnnotation( importType( "jakarta.persistence.OneToMany") ); + ab.addAttribute( "cascade", getCascadeTypes( property ) ); + ab.addAttribute( "fetch", getFetchType (property) ); + if ( collection.isInverse() ) { + mappedBy = getOneToManyMappedBy( md, collection ); + ab.addQuotedAttribute( "mappedBy", mappedBy ); + } + annotation.append( ab.getResult() ); + + if (mappedBy == null) annotation.append("\n").append( generateJoinColumnsAnnotation(property, md) ); + } + else { + //TODO do the @OneToMany @JoinTable + //TODO composite element + String mappedBy = null; + AnnotationBuilder ab = AnnotationBuilder.createAnnotation( importType( "jakarta.persistence.ManyToMany") ); + ab.addAttribute( "cascade", getCascadeTypes( property ) ); + ab.addAttribute( "fetch", getFetchType (property) ); + + if ( collection.isInverse() ) { + mappedBy = getManyToManyMappedBy( md, collection ); + ab.addQuotedAttribute( "mappedBy", mappedBy ); + } + annotation.append(ab.getResult()); + if (mappedBy == null) { + annotation.append("\n @"); + annotation.append( importType( "jakarta.persistence.JoinTable") ).append( "(name=\"" ); + Table table = collection.getCollectionTable(); + + annotation.append( table.getName() ); + annotation.append( "\"" ); + if ( StringHelper.isNotEmpty( table.getSchema() ) ) { + annotation.append(", schema=\"").append( table.getSchema() ).append("\""); + } + if ( StringHelper.isNotEmpty( table.getCatalog() ) ) { + annotation.append(", catalog=\"").append( table.getCatalog() ).append("\""); + } + String uniqueConstraint = generateAnnTableUniqueConstraint(table); + if (!uniqueConstraint.isEmpty()) { + annotation.append(", uniqueConstraints=").append(uniqueConstraint); + } + annotation.append( ", joinColumns = { "); + buildArrayOfJoinColumnAnnotation( + collection.getKey().getSelectables().iterator(), + null, + annotation, + property.isInsertable(), + property.isUpdatable() + ); + annotation.append( " }"); + annotation.append( ", inverseJoinColumns = { "); + buildArrayOfJoinColumnAnnotation( + collection.getElement().getSelectables().iterator(), + null, + annotation, + property.isInsertable(), + property.isUpdatable() + ); + annotation.append( " }"); + annotation.append(")"); + } + + } + String hibernateCascade = getHibernateCascadeTypeAnnotation( property ); + if (!hibernateCascade.isEmpty()) annotation.append("\n ").append(hibernateCascade); + } + return annotation.toString(); + } + + private String getManyToManyMappedBy(Metadata md, Collection collection) { + String mappedBy; + Iterator joinColumnsIt = collection.getKey().getSelectables().iterator(); + Set joinColumns = new HashSet(); + while ( joinColumnsIt.hasNext() ) { + joinColumns.add( joinColumnsIt.next() ); + } + ManyToOne manyToOne = (ManyToOne) collection.getElement(); + PersistentClass pc = md.getEntityBinding(manyToOne.getReferencedEntityName()); + Iterator properties = pc.getProperties().iterator(); + //TODO we should check the table too + boolean isOtherSide = false; + mappedBy = "unresolved"; + while ( ! isOtherSide && properties.hasNext() ) { + Property collectionProperty = properties.next(); + Value collectionValue = collectionProperty.getValue(); + if (collectionValue instanceof Collection realCollectionValue) { + if ( ! realCollectionValue.isOneToMany() ) { + if ( joinColumns.size() == realCollectionValue.getElement().getColumnSpan() ) { + isOtherSide = true; + for (Selectable selectable : realCollectionValue.getElement().getSelectables()) { + if (!joinColumns.contains(selectable)) { + isOtherSide = false; + break; + } + } + if (isOtherSide) { + mappedBy = collectionProperty.getName(); + } + } + } + } + } + return mappedBy; + } + + private String getOneToManyMappedBy(Metadata md, Collection collection) { + String mappedBy; + Iterator joinColumnsIt = collection.getKey().getSelectables().iterator(); + Set joinColumns = new HashSet(); + while ( joinColumnsIt.hasNext() ) { + joinColumns.add( joinColumnsIt.next() ); + } + OneToMany oneToMany = (OneToMany) collection.getElement(); + PersistentClass pc = md.getEntityBinding(oneToMany.getReferencedEntityName()); + Iterator properties = pc.getProperties().iterator(); + //TODO we should check the table too + boolean isOtherSide = false; + mappedBy = "unresolved"; + while ( ! isOtherSide && properties.hasNext() ) { + Property manyProperty = properties.next(); + Value manyValue = manyProperty.getValue(); + if (manyValue instanceof ManyToOne) { + if ( joinColumns.size() == manyValue.getColumnSpan() ) { + isOtherSide = true; + for (Selectable selectable : manyValue.getSelectables()) { + if (!joinColumns.contains(selectable)) { + isOtherSide = false; + break; + } + } + if (isOtherSide) { + mappedBy = manyProperty.getName(); + } + } + + } + } + return mappedBy; + } + + private String getOneToOneMappedBy(Metadata md, OneToOne oneToOne) { + String mappedBy; + Iterator joinSelectablesIt = oneToOne.getSelectables().iterator(); + Set joinSelectables = new HashSet(); + while ( joinSelectablesIt.hasNext() ) { + joinSelectables.add( joinSelectablesIt.next() ); + } + PersistentClass pc = md.getEntityBinding(oneToOne.getReferencedEntityName()); + String referencedPropertyName = oneToOne.getReferencedPropertyName(); + if ( referencedPropertyName != null ) + return referencedPropertyName; + + Iterator properties = pc.getProperties().iterator(); + //TODO we should check the table too + boolean isOtherSide = false; + mappedBy = "unresolved"; + + + while ( ! isOtherSide && properties.hasNext() ) { + Property oneProperty = properties.next(); + Value manyValue = oneProperty.getValue(); + if ((manyValue instanceof OneToOne || manyValue instanceof ManyToOne)) { + if ( joinSelectables.size() == manyValue.getColumnSpan() ) { + isOtherSide = true; + for (Selectable selectable : manyValue.getSelectables()) { + if (!joinSelectables.contains(selectable)) { + isOtherSide = false; + break; + } + } + if (isOtherSide) { + mappedBy = oneProperty.getName(); + } + } + + } + } + return mappedBy; + } + + public boolean isSubclass() { + return clazz.getSuperclass()!=null; + } + + public List getPropertyClosureForFullConstructor() { + return getPropertyClosureForFullConstructor(clazz); + } + + protected List getPropertyClosureForFullConstructor(PersistentClass pc) { + List l = new ArrayList(getPropertyClosureForSuperclassFullConstructor( pc )); + l.addAll(getPropertiesForFullConstructor( pc )); + return l; + } + + public List getPropertiesForFullConstructor() { + return getPropertiesForFullConstructor(clazz); + } + + protected List getPropertiesForFullConstructor(PersistentClass pc) { + List result = new ArrayList(); + + for ( Iterator myFields = getAllPropertiesIterator(pc); myFields.hasNext() ; ) { + Property field = (Property) myFields.next(); + // TODO: if(!field.isGenerated() ) ) { + if(field.equals(pc.getIdentifierProperty()) && !isAssignedIdentifier(pc, field)) { + continue; // dont add non assigned identifiers + } else if(field.equals(pc.getVersion())) { + continue; // version prop + } else if(field.isBackRef()) { + continue; + } else if(isFormula(field)) { + continue; + } else { + result.add( field ); + } + } + + return result; + } + + private boolean isFormula(Property field) { + Value value = field.getValue(); + boolean foundFormula = false; + + if(value!=null && value.getColumnSpan()>0) { + for (Selectable element : value.getSelectables()) { + if (!(element instanceof Formula)) { + return false; + } else { + foundFormula = true; + } + } + } else { + return false; + } + return foundFormula; + } + + public List getPropertyClosureForSuperclassFullConstructor() { + return getPropertyClosureForSuperclassFullConstructor(clazz); + } + + public List getPropertyClosureForSuperclassFullConstructor(PersistentClass pc) { + List result = new ArrayList(); + if ( pc.getSuperclass() != null ) { + // The correct sequence is vital here, as the subclass should be + // able to invoke the fullconstructor based on the sequence returned + // by this method! + result.addAll( getPropertyClosureForSuperclassFullConstructor( pc.getSuperclass() ) ); + result.addAll( getPropertiesForFullConstructor( pc.getSuperclass() ) ); + } + + return result; + } + + + public List getPropertyClosureForMinimalConstructor() { + return getPropertyClosureForMinimalConstructor(clazz); + } + + protected List getPropertyClosureForMinimalConstructor(PersistentClass pc) { + List l = new ArrayList(getPropertyClosureForSuperclassMinConstructor( pc )); + l.addAll(getPropertiesForMinimalConstructor( pc )); + return l; + } + + public List getPropertiesForMinimalConstructor() { + return getPropertiesForMinimalConstructor(clazz); + } + + protected List getPropertiesForMinimalConstructor(PersistentClass pc) { + List result = new ArrayList(); + + for ( Iterator myFields = getAllPropertiesIterator(pc); myFields.hasNext() ; ) { + Property property = (Property) myFields.next(); + if(property.equals(pc.getIdentifierProperty())) { + if(isAssignedIdentifier(pc, property)) { + result.add(property); + } else { + continue; + } + } else if (property.equals(pc.getVersion())) { + continue; // the version property should not be in the result. + } else if( isRequiredInConstructor(property) ) { + result.add(property); + } + } + + return result; + } + + protected boolean isAssignedIdentifier(PersistentClass pc, Property property) { + if(property.equals(pc.getIdentifierProperty())) { + if(property.getValue() instanceof EnhancedValue sv) { + return "assigned".equals(sv.getIdentifierGeneratorStrategy()); + } else if (property.getValue().isSimpleValue()) { + ValueUtil v = new ValueUtil((SimpleValue)property.getValue()); + return "assigned".equals(v.getIdentifierGeneratorStrategy()); + } + } + return false; + } + + public List getPropertyClosureForSuperclassMinimalConstructor() { + return getPropertyClosureForSuperclassMinConstructor(clazz); + } + + protected List getPropertyClosureForSuperclassMinConstructor(PersistentClass pc) { + List result = new ArrayList(); + if ( pc.getSuperclass() != null ) { + // The correct sequence is vital here, as the subclass should be + // able to invoke the fullconstructor based on the sequence returned + // by this method! + result.addAll( getPropertyClosureForSuperclassMinConstructor( pc.getSuperclass() ) ); + result.addAll( getPropertiesForMinimalConstructor( pc.getSuperclass() ) ); + } + + return result; + } + + public POJOClass getSuperClass(){ + if (!isSubclass()) + return null; + return new EntityPOJOClass(clazz.getSuperclass(),c2j); + } + + + public String toString() { + return "Entity: " + (clazz==null?"":clazz.getEntityName()); + } + + public boolean hasVersionProperty() { + return clazz.isVersioned() && clazz instanceof RootClass; + } + + /* + * @see org.hibernate.tool.hbm2x.pojo.POJOClass#getVersionProperty() + */ + public Property getVersionProperty() + { + return clazz.getVersion(); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ImportContext.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ImportContext.java new file mode 100644 index 000000000000..d651c4af61fb --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ImportContext.java @@ -0,0 +1,39 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +public interface ImportContext { + + /** + * Add fqcn to the import list. Returns fqcn as needed in source code. + * Attempts to handle fqcn with array and generics references. + *

+ * e.g. + * java.util.Collection imports java.util.Collection and returns Collection + * org.marvel.Hulk[] imports org.marvel.Hulk and returns Hulk + * + * + * @return import string + */ + public abstract String importType(String fqcn); + + public abstract String staticImport(String fqcn, String member); + + public abstract String generateImports(); + +} \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ImportContextImpl.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ImportContextImpl.java new file mode 100644 index 000000000000..5dcda4fb2b2e --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/ImportContextImpl.java @@ -0,0 +1,155 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +import org.hibernate.internal.util.StringHelper; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +public class ImportContextImpl implements ImportContext { + + Set imports = new TreeSet(); + Set staticImports = new TreeSet(); + Map simpleNames = new HashMap(); + + String basePackage = ""; + + // TODO: share this somehow, redundant from Cfg2JavaTool + private static final Map PRIMITIVES = new HashMap(); + static { + PRIMITIVES.put( "char", "Character" ); + + PRIMITIVES.put( "byte", "Byte" ); + PRIMITIVES.put( "short", "Short" ); + PRIMITIVES.put( "int", "Integer" ); + PRIMITIVES.put( "long", "Long" ); + + PRIMITIVES.put( "boolean", "Boolean" ); + + PRIMITIVES.put( "float", "Float" ); + PRIMITIVES.put( "double", "Double" ); + + } + + public ImportContextImpl(String basePackage) { + this.basePackage = basePackage; + } + + /** + * Add fqcn to the import list. Returns fqcn as needed in source code. + * Attempts to handle fqcn with array and generics references. + *

+ * e.g. + * java.util.Collection imports java.util.Collection and returns Collection + * org.marvel.Hulk[] imports org.marvel.Hulk and returns Hulk + * + * + * @return import string + */ + public String importType(String fqcn) { + String result = fqcn; + + String additionalTypePart = null; + if(fqcn.indexOf('<')>=0) { + additionalTypePart = result.substring(fqcn.indexOf('<')); + result = result.substring(0,fqcn.indexOf('<')); + fqcn = result; + } else if(fqcn.indexOf('[')>=0) { + additionalTypePart = result.substring(fqcn.indexOf('[')); + result = result.substring(0,fqcn.indexOf('[')); + fqcn = result; + } + + String pureFqcn = fqcn.replace( '$', '.' ); + + boolean canBeSimple = true; + + + String simpleName = StringHelper.unqualify(fqcn); + if(simpleNames.containsKey(simpleName)) { + String existingFqcn = (String) simpleNames.get(simpleName); + canBeSimple = existingFqcn.equals(pureFqcn); + } else { + simpleNames.put(simpleName, pureFqcn); + imports.add( pureFqcn ); + } + + + if ( inSamePackage(fqcn) || (imports.contains( pureFqcn ) && canBeSimple) ) { + result = StringHelper.unqualify( result ); // dequalify + } else if ( inJavaLang( fqcn ) ) { + result = result.substring( "java.lang.".length() ); + } + + if(additionalTypePart!=null) { + result = result + additionalTypePart; + } + + result = result.replace( '$', '.' ); + return result; + } + + public String staticImport(String fqcn, String member) { + String local = fqcn + "." + member; + imports.add(local); + staticImports.add(local); + + if(member.equals("*")) { + return ""; + } else { + return member; + } + } + + private boolean inDefaultPackage(String className) { + return !className.contains("."); + } + + private boolean isPrimitive(String className) { + return PRIMITIVES.containsKey( className ); + } + + private boolean inSamePackage(String className) { + return StringHelper.qualifier( className ).equals(basePackage); + } + + private boolean inJavaLang(String className) { + return "java.lang".equals( StringHelper.qualifier( className ) ); + } + + public String generateImports() { + StringBuilder buf = new StringBuilder(); + for (String next : imports) { + if (!(isPrimitive(next) || inDefaultPackage(next) || inJavaLang(next) || inSamePackage(next))) { + if (staticImports.contains(next)) { + buf.append("import static ").append(next).append(";\r\n"); + } else { + buf.append("import ").append(next).append(";\r\n"); + } + } + } + + if(buf.indexOf( "$" )>=0) { + return buf.toString(); + } + return buf.toString(); + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/JavaExporter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/JavaExporter.java new file mode 100644 index 000000000000..1d6e2d6635ad --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/JavaExporter.java @@ -0,0 +1,52 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +import org.hibernate.tool.reveng.internal.export.common.GenericExporter; + +/** + * @author max + */ +public class JavaExporter extends GenericExporter { + + private static final String POJO_JAVACLASS_FTL = "pojo/Pojo.ftl"; + + protected void init() { + getProperties().put(TEMPLATE_NAME, POJO_JAVACLASS_FTL); + getProperties().put(FILE_PATTERN, "{package-name}/{class-name}.java"); + } + + public JavaExporter() { + init(); + } + + public String getName() { + return "hbm2java"; + } + + protected void setupContext() { + //TODO: this safe guard should be in the root templates instead for each variable they depend on. + if(!getProperties().containsKey("ejb3")) { + getProperties().put("ejb3", "false"); + } + if(!getProperties().containsKey("jdk5")) { + getProperties().put("jdk5", "false"); + } + super.setupContext(); + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/JavaTypeFromValueVisitor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/JavaTypeFromValueVisitor.java new file mode 100644 index 000000000000..e720cbf3d614 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/JavaTypeFromValueVisitor.java @@ -0,0 +1,118 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +import org.hibernate.HibernateException; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.ManyToOne; +import org.hibernate.mapping.Map; +import org.hibernate.mapping.OneToMany; +import org.hibernate.mapping.OneToOne; +import org.hibernate.mapping.Set; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.mapping.ToOne; +import org.hibernate.mapping.Value; +import org.hibernate.tool.reveng.internal.export.common.DefaultValueVisitor; +import org.hibernate.type.CustomType; +import org.hibernate.type.Type; + +public class JavaTypeFromValueVisitor extends DefaultValueVisitor { + + + private boolean preferRawTypeNames = true; + + public JavaTypeFromValueVisitor() { + super( true ); + } + + // special handling for Map's to avoid initialization of comparators that depends on the keys/values which might not be generated yet. + public Object accept(Map o) { + if ( o.isSorted() ) { + return "java.util.SortedMap"; + } + return super.accept(o); + } + + // special handling for Set's to avoid initialization of comparators that depends on the keys/values which might not be generated yet. + public Object accept(Set o) { + if ( o.isSorted() ) { + return "java.util.SortedSet"; + } + return super.accept(o); + } + + public Object accept(Component value) { + // composite-element breaks without it. + return value.getComponentClassName(); + } + + public Object accept(OneToOne o) { + return acceptToOne(o); + } + + public Object accept(ManyToOne o) { + return acceptToOne(o); + } + + private Object acceptToOne(ToOne value) { + return value.getReferencedEntityName(); // should get the cfg and lookup the persistenclass. + } + + public Object accept(OneToMany value) { + return value.getAssociatedClass().getClassName(); + } + + private String toName(Class c) { + + if ( c.isArray() ) { + Class a = c.getComponentType(); + + return a.getName() + "[]"; + } + else { + return c.getName(); + } + } + + protected Object handle(Value o) { + Value value = (Value) o; + try { + // have to attempt calling gettype to decide if its custom type. + Type type = value.getType(); + if(type instanceof CustomType) { + return toName( type.getReturnedClass() ); + } + } catch(HibernateException he) { + // ignore + } + + if ( preferRawTypeNames && value.isSimpleValue() ) { + // this logic make us use the raw typename if it is something else than an Hibernate type. So, if user wrote long we will use long...if he meant to have a Long then he should use the java.lang.Long version. + String typename = ( (SimpleValue) value ).getTypeName(); + if ( !Cfg2JavaTool.isNonPrimitiveTypeName( typename ) ) { + String val = ( (SimpleValue) value ).getTypeName(); + if(val!=null) return val; // val can be null when type is any + } + } + + return toName( value.getType().getReturnedClass() ); + + } + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/MetaAttributeConstants.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/MetaAttributeConstants.java new file mode 100644 index 000000000000..8cd2d2fdddb0 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/MetaAttributeConstants.java @@ -0,0 +1,30 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +public interface MetaAttributeConstants { + + static final String GENERATED_CLASS = "generated-class"; + static final String INTERFACE = "interface"; + static final String CLASS_MODIFIER = "class-modifier"; + static final String SCOPE_CLASS = "scope-class"; + static final String CLASS_DESCRIPTION = "class-description"; + static final String IMPLEMENTS = "implements"; + static final String EXTENDS = "extends"; + static final String GEN_PROPERTY = "gen-property"; // in templates. +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/MetaAttributeHelper.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/MetaAttributeHelper.java new file mode 100644 index 000000000000..42258bedda50 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/MetaAttributeHelper.java @@ -0,0 +1,87 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +import org.hibernate.mapping.MetaAttribute; + +import java.util.Collection; +import java.util.Iterator; + +/** + * Helper for loading, merging and accessing tags. + * + * @author max + * + * + */ +public final class MetaAttributeHelper { + + private MetaAttributeHelper() { + //noop + } + + /** + * @param meta + * @param separator + */ + public static String getMetaAsString(Collection meta, String separator) { + if(meta==null || meta.isEmpty() ) { + return ""; + } + StringBuffer buf = new StringBuffer(); + + for (Iterator iter = meta.iterator(); iter.hasNext();) { + buf.append(iter.next() ); + if(iter.hasNext() ) buf.append(separator); + } + return buf.toString(); + } + + public static String getMetaAsString(MetaAttribute meta, String seperator) { + if(meta==null) { + return null; + } + else { + return getMetaAsString(meta.getValues(),seperator); + } + } + + static boolean getMetaAsBool(Collection c, boolean defaultValue) { + if(c==null || c.isEmpty() ) { + return defaultValue; + } + else { + return Boolean.valueOf(c.iterator().next().toString() ).booleanValue(); + } + } + + public static String getMetaAsString(MetaAttribute c) { + return c==null?"":getMetaAsString(c.getValues() ); + } + + static String getMetaAsString(Collection c) { + return getMetaAsString(c, ""); + } + + public static boolean getMetaAsBool(MetaAttribute metaAttribute, boolean defaultValue) { + return getMetaAsBool(metaAttribute==null?null:metaAttribute.getValues(), defaultValue); + } + + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/NoopImportContext.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/NoopImportContext.java new file mode 100644 index 000000000000..f7db7ee528e7 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/NoopImportContext.java @@ -0,0 +1,34 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +public class NoopImportContext implements ImportContext { + + public String importType(String fqcn) { + return fqcn; + } + + public String staticImport(String fqcn, String member) { + return fqcn; + } + + public String generateImports() { + return ""; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/POJOClass.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/POJOClass.java new file mode 100644 index 000000000000..91f078c164db --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/java/POJOClass.java @@ -0,0 +1,125 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.java; + +import org.hibernate.mapping.Property; + +import java.util.Iterator; +import java.util.List; + +/** + * Wrapper class over PersistentClass used in hbm2java and hbm2doc tool + * @author max + * @author Amit Bhayani + * + */ +public interface POJOClass extends ImportContext { + + /** + * Returns "package packagename;" where packagename is either the declared packagename, + * or the one provide via meta attribute "generated-class". + * + * Returns "// default package" if no package declarition available. + * + */ + public String getPackageDeclaration(); + + public String getClassModifiers(); + + public String getQualifiedDeclarationName(); + + /** + * Returns the javadoc associated with the class. + * + * @param fallback the default text if nothing else is found + * @param indent how many spaces should be added + * @return + */ + public String getClassJavaDoc(String fallback, int indent); + + /** + * + * @return declaration type "interface" or "class" + */ + public String getDeclarationType(); + + /** + * @return unqualified classname for this class (can be changed by meta attribute "generated-class") + */ + public String getDeclarationName(); + + public String getImplementsDeclaration(); + public String getImplements(); + + public String getExtendsDeclaration(); + public String getExtends(); + + public String generateEquals(String thisName, String otherName, boolean useGenerics); + + public boolean isComponent(); + + public String getExtraClassCode(); + + public boolean needsEqualsHashCode(); + + public boolean hasIdentifierProperty(); + + public String generateAnnColumnAnnotation(Property property); + public String generateAnnIdGenerator(); + public String generateAnnTableUniqueConstraint(); + public String generateBasicAnnotation(Property property); + public Iterator getAllPropertiesIterator(); + + public String getPackageName(); + public String getShortName(); + + public Iterator getToStringPropertiesIterator(); + public Iterator getEqualsHashCodePropertiesIterator(); + + public boolean needsToString(); + + public String getFieldJavaDoc(Property property, int indent); + public String getFieldDescription(Property property); + + public Object getDecoratedObject(); + + public boolean isInterface(); + + public boolean isSubclass(); + + public List getPropertiesForFullConstructor(); + public List getPropertyClosureForFullConstructor(); + public List getPropertyClosureForSuperclassFullConstructor(); + + public boolean needsMinimalConstructor(); + public boolean needsFullConstructor(); + public List getPropertiesForMinimalConstructor(); + public List getPropertyClosureForMinimalConstructor(); + public List getPropertyClosureForSuperclassMinimalConstructor(); + + public POJOClass getSuperClass(); + + public String getJavaTypeName(Property p, boolean useGenerics); + public String getFieldInitialization(Property p, boolean useGenerics); + + public Property getIdentifierProperty(); + + public boolean hasVersionProperty(); + public Property getVersionProperty(); + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/BadCachingDetector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/BadCachingDetector.java new file mode 100644 index 000000000000..d2bcba07b4e0 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/BadCachingDetector.java @@ -0,0 +1,52 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Value; +import org.hibernate.tool.reveng.internal.export.common.EntityNameFromValueVisitor; + +public class BadCachingDetector extends EntityModelDetector { + + public String getName() { + return "cache"; + } + + @Override + protected void visitProperty(Property property, IssueCollector collector) { + Value value = property.getValue(); + + if(value instanceof Collection col) { + if(col.getCacheConcurrencyStrategy()!=null) { // caching is enabled + if (!col.getElement().isSimpleValue()) { + String entityName = (String) col.getElement().accept( new EntityNameFromValueVisitor() ); + + if(entityName!=null) { + PersistentClass classMapping = getMetadata().getEntityBinding(entityName); + if(classMapping.getCacheConcurrencyStrategy()==null) { + collector.reportIssue( new Issue("CACHE_COLLECTION_NONCACHABLE_TARGET", Issue.HIGH_PRIORITY, "Entity '" + classMapping.getEntityName() +"' is referenced from the cache-enabled collection '" + col.getRole() + "' without the entity being cachable")); + } + } + } + } + } + } +} + diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/Detector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/Detector.java new file mode 100644 index 000000000000..e32727398be4 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/Detector.java @@ -0,0 +1,37 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +import org.hibernate.boot.Metadata; + +public abstract class Detector { + + private Metadata metadata; + + public void initialize(Metadata metadata) { + this.metadata = metadata; + } + + protected Metadata getMetadata() { + return metadata; + } + + abstract public void visit(IssueCollector collector); + + abstract public String getName(); +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/EntityModelDetector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/EntityModelDetector.java new file mode 100644 index 000000000000..abf7170a12b4 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/EntityModelDetector.java @@ -0,0 +1,46 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; + +public abstract class EntityModelDetector extends Detector { + + public void visit(IssueCollector collector) { + for (PersistentClass clazz : getMetadata().getEntityBindings()) { + this.visit(clazz, collector); + } + } + + protected void visit(PersistentClass clazz, IssueCollector collector) { + visitProperties(clazz, collector ); + } + + private void visitProperties(PersistentClass clazz, IssueCollector collector) { + if(clazz.hasIdentifierProperty()) { + this.visitProperty(clazz.getIdentifierProperty(), collector); + } + for (Property property : clazz.getProperties()) { + this.visitProperty(property, collector); + } + } + + protected abstract void visitProperty(Property property, IssueCollector collector); + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/HbmLint.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/HbmLint.java new file mode 100644 index 000000000000..239b8179c3bd --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/HbmLint.java @@ -0,0 +1,66 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +import org.hibernate.boot.Metadata; + +import java.util.ArrayList; +import java.util.List; + +public class HbmLint implements IssueCollector { + + + final Detector[] detectors; + + public HbmLint(Detector[] detectors) { + this.detectors = detectors; + } + + List results = new ArrayList(); + + public void analyze(Metadata metadata) { + for (int i = 0; i < detectors.length; i++) { + detectors[i].initialize(metadata); + detectors[i].visit(this); + } + + } + + /* (non-Javadoc) + * @see org.hibernate.tool.hbmlint.IssueCollector#reportProblem(org.hibernate.tool.hbmlint.Issue) + */ + public void reportIssue(Issue analyze) { + results.add(analyze); + } + + public List getResults() { + return results; + } + + public static HbmLint createInstance() { + return new HbmLint( + new Detector[] { + new BadCachingDetector(), + new InstrumentationDetector(), + new ShadowedIdentifierDetector(), + new SchemaByMetaDataDetector() + }); + + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/HbmLintExporter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/HbmLintExporter.java new file mode 100644 index 000000000000..6e115df3c485 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/HbmLintExporter.java @@ -0,0 +1,43 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +import org.hibernate.tool.reveng.internal.export.common.GenericExporter; + +public class HbmLintExporter extends GenericExporter { + + private static final String TEXT_REPORT_FTL = "lint/text-report.ftl"; + + public void start() { + getProperties().put(TEMPLATE_NAME, TEXT_REPORT_FTL); + getProperties().put(FILE_PATTERN, "hbmlint-result.txt"); + super.start(); + } + protected void setupContext() { + HbmLint hbmlint = HbmLint.createInstance(); + hbmlint.analyze( getMetadata() ); + getProperties().put("lintissues", hbmlint.getResults()); + super.setupContext(); + } + + public String getName() { + return "hbmlint"; + } + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/InstrumentationDetector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/InstrumentationDetector.java new file mode 100644 index 000000000000..c0d97903a472 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/InstrumentationDetector.java @@ -0,0 +1,93 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +import org.hibernate.MappingException; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.bytecode.spi.BytecodeProvider; +import org.hibernate.engine.spi.Managed; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; + +public class InstrumentationDetector extends EntityModelDetector { + + public String getName() { + return "instrument"; + } + + private boolean enhanceEnabled; + + public void initialize(Metadata metadata) { + super.initialize(metadata); + if (metadata instanceof MetadataImplementor) { + final BytecodeProvider bytecodeProvider = + ((MetadataImplementor)metadata).getMetadataBuildingOptions().getServiceRegistry() + .getService( BytecodeProvider.class ); + if(bytecodeProvider != null + && !(bytecodeProvider instanceof org.hibernate.bytecode.internal.none.BytecodeProviderImpl)) { + enhanceEnabled = true; + } + } + } + + protected void visit(PersistentClass clazz, IssueCollector collector) { + Class mappedClass; + try { + mappedClass = clazz.getMappedClass(); + } catch(MappingException me) { + // ignore + return; + } + + if(clazz.isLazy()) { + try { + mappedClass.getConstructor( new Class[0] ); + } + catch (SecurityException e) { + // ignore + } + catch (NoSuchMethodException e) { + collector.reportIssue(new Issue("LAZY_NO_DEFAULT_CONSTRUCTOR",Issue.NORMAL_PRIORITY, "lazy='true' set for '" + clazz.getEntityName() +"', but class has no default constructor." )); + return; + } + + } else if(enhanceEnabled){ + Class[] interfaces = mappedClass.getInterfaces(); + boolean enhanced = false; + for (Class intface : interfaces) { + if (intface.getName().equals(Managed.class.getName())) { + enhanced = true; + break; + } + } + + if (!enhanced) { + collector.reportIssue( new Issue("LAZY_NOT_INSTRUMENTED", Issue.HIGH_PRIORITY, "'" + clazz.getEntityName() + "' has lazy='false', but its class '" + mappedClass.getName() + "' has not been instrumented with javaassist") ); + return; + } + + } + } + + @Override + protected void visitProperty( + Property property, + IssueCollector collector) { + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/Issue.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/Issue.java new file mode 100644 index 000000000000..4cc3da858b22 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/Issue.java @@ -0,0 +1,48 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +public class Issue { + + public static final int HIGH_PRIORITY = 100; + public static final int NORMAL_PRIORITY = 50; + public static final int LOW_PRIORITY = 0; + + private final String type; + private final int priority; + + private final String description; + + public Issue(String type, int priority, String description) { + this.description = description; + this.priority = priority; + this.type = type; + } + + public String toString() { + return type + ":" + description; + } + + public String getDescription() { + return description; + } + + public int getPriority() { + return priority; + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/IssueCollector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/IssueCollector.java new file mode 100644 index 000000000000..b0991941d6a7 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/IssueCollector.java @@ -0,0 +1,24 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2018-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +public interface IssueCollector { + + public abstract void reportIssue(Issue analyze); + +} \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/RelationalModelDetector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/RelationalModelDetector.java new file mode 100644 index 000000000000..8696b60ec0f7 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/RelationalModelDetector.java @@ -0,0 +1,50 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Table; + +import java.util.Iterator; + +public abstract class RelationalModelDetector extends Detector { + + public void visit(IssueCollector collector) { + for (Iterator

iter = getMetadata().collectTableMappings().iterator(); iter.hasNext();) { + Table table = (Table) iter.next(); + this.visit(table, collector); + } + } + + abstract protected void visit(Table table, Column col, IssueCollector collector); + + protected void visitColumns(Table table, IssueCollector collector) { + for (Column col : table.getColumns()) { + this.visit(table, col, collector ); + } + } + + /** + * @return true if visit should continue down through the columns + */ + protected void visit(Table table, IssueCollector collector) { + visitColumns(table, collector); + } + +} + diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/SchemaByMetaDataDetector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/SchemaByMetaDataDetector.java new file mode 100644 index 000000000000..8709c09983c9 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/SchemaByMetaDataDetector.java @@ -0,0 +1,336 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +import org.hibernate.HibernateException; +import org.hibernate.MappingException; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Environment; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.generator.Generator; +import org.hibernate.id.PersistentIdentifierGenerator; +import org.hibernate.id.enhanced.SequenceStyleGenerator; +import org.hibernate.id.enhanced.TableGenerator; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.IdentifierCollection; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.RootClass; +import org.hibernate.mapping.Table; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.tool.reveng.api.core.RevengDialect; +import org.hibernate.tool.reveng.api.core.RevengDialectFactory; +import org.hibernate.tool.reveng.api.core.RevengStrategy.SchemaSelection; +import org.hibernate.tool.reveng.internal.core.RevengMetadataCollector; +import org.hibernate.tool.reveng.internal.core.reader.DatabaseReader; +import org.hibernate.tool.reveng.internal.core.strategy.DefaultStrategy; +import org.hibernate.tool.reveng.internal.core.strategy.TableSelectorStrategy; +import org.hibernate.tool.reveng.internal.util.JdbcToHibernateTypeHelper; +import org.hibernate.tool.reveng.internal.util.TableNameQualifier; +import org.hibernate.type.MappingContext; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Properties; +import java.util.Set; +import java.util.TreeMap; + +@SuppressWarnings("deprecation") +public class SchemaByMetaDataDetector extends RelationalModelDetector { + + public String getName() { + return "schema"; + } + + DatabaseReader reader; + + private SequenceCollector sequenceCollector; + + private TableSelectorStrategy tableSelector; + + private RevengDialect metadataDialect; + + private Dialect dialect; + + private MappingContext mapping; + + private Properties properties; + + /** current table as read from the database */ + Table currentDbTable = null; + + public void initialize(Metadata metadata) { + super.initialize( metadata); + StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder(); + ServiceRegistry serviceRegistry = builder.build(); + + properties = Environment.getProperties(); + dialect = serviceRegistry.getService(JdbcServices.class).getDialect(); + + tableSelector = new TableSelectorStrategy( + new DefaultStrategy() ); + metadataDialect = RevengDialectFactory + .createMetaDataDialect( + dialect, + properties ); + reader = DatabaseReader.create( + properties, + tableSelector, + metadataDialect, + serviceRegistry); + ConnectionProvider connectionProvider = serviceRegistry.getService(ConnectionProvider.class); + sequenceCollector = SequenceCollector.create(connectionProvider); + } + + public void visit(IssueCollector collector) { + super.visit(collector); + visitGenerators(collector); + } + + public void visitGenerators(IssueCollector collector) { + Iterator iter = iterateGenerators(); + + Set sequences = Collections.EMPTY_SET; + if(dialect.getSequenceSupport().supportsSequences()) { + sequences = sequenceCollector.readSequences(dialect.getQuerySequencesString()); + } + + // TODO: move this check into something that could check per class or collection instead. + while ( iter.hasNext() ) { + PersistentIdentifierGenerator generator = (PersistentIdentifierGenerator) iter.next(); + Object key = getGeneratorKey(generator); + if ( !isSequence(key, sequences) && !isTable( key ) ) { + collector.reportIssue( new Issue( "MISSING_ID_GENERATOR", Issue.HIGH_PRIORITY, "Missing sequence or table: " + key)); + } + } + + } + + private boolean isSequence(Object key, Set sequences) { + if(key instanceof String) { + if ( sequences.contains( key ) ) { + return true; + } else { + String[] strings = StringHelper.split(".", (String) key); + if(strings.length==3) { + return sequences.contains(strings[2]); + } else if (strings.length==2) { + return sequences.contains(strings[1]); + } + } + } + return false; + } + + private boolean isTable(Object key) throws HibernateException { + // BIG HACK - should probably utilize the table cache before going to the jdbcreader :( + if(key instanceof String) { + String[] strings = StringHelper.split(".", (String) key); + if(strings.length==1) { + tableSelector.clearSchemaSelections(); + tableSelector.addSchemaSelection( createSchemaSelection(null,null, strings[0]) ); + Collection
collection = readFromDatabase(); + return !collection.isEmpty(); + } else if(strings.length==3) { + tableSelector.clearSchemaSelections(); + tableSelector.addSchemaSelection( createSchemaSelection(strings[0],strings[1], strings[2]) ); + Collection
collection = readFromDatabase(); + return !collection.isEmpty(); + } else if (strings.length==2) { + tableSelector.clearSchemaSelections(); + tableSelector.addSchemaSelection( createSchemaSelection(null,strings[0], strings[1]) ); + Collection
collection = readFromDatabase(); + return !collection.isEmpty(); + } + } + return false; + } + + public void visit(Table table, IssueCollector pc) { + + if ( table.isPhysicalTable() ) { + setSchemaSelection( table ); + + Collection
collection = readFromDatabase(); + + if ( collection.isEmpty() ) { + pc.reportIssue( new Issue( "SCHEMA_TABLE_MISSING", + Issue.HIGH_PRIORITY, "Missing table " + + TableNameQualifier.qualify( table.getCatalog(), table + .getSchema(), table.getName() ) ) ); + return; + } + else if ( collection.size() > 1 ) { + pc.reportIssue( new Issue( "SCHEMA_TABLE_MISSING", + Issue.NORMAL_PRIORITY, "Found " + + collection.size() + + " tables for " + + TableNameQualifier.qualify( table.getCatalog(), table + .getSchema(), table.getName() ) ) ); + return; + } + else { + currentDbTable = collection.iterator().next(); + visitColumns(table,pc); + } + } + else { + // log? + } + } + + String table(Table t) { + return TableNameQualifier.qualify( t.getCatalog(), t.getSchema(), t.getName() ); + } + + public void visit( + Table table, + Column col, + IssueCollector pc) { + if ( currentDbTable == null ) { + return; + } + + Column dbColumn = currentDbTable + .getColumn( new Column( col.getName() ) ); + + if ( dbColumn == null ) { + pc.reportIssue( new Issue( "SCHEMA_COLUMN_MISSING", + Issue.HIGH_PRIORITY, table(table) + " is missing column: " + col.getName() ) ); + } + else { + //TODO: this needs to be able to know if a type is truly compatible or not. Right now it requires an exact match. + //String sqlType = col.getSqlType( dialect, mapping ); + int dbTypeCode = dbColumn.getSqlTypeCode().intValue(); + int modelTypeCode = col + .getSqlTypeCode( mapping ); + // TODO: sqltype name string + if ( !(dbTypeCode == modelTypeCode ) ) { + pc.reportIssue( new Issue( "SCHEMA_COLUMN_TYPE_MISMATCH", + Issue.NORMAL_PRIORITY, table(table) + " has a wrong column type for " + + col.getName() + ", expected: " + + JdbcToHibernateTypeHelper.getJDBCTypeName(modelTypeCode) + " but was " + JdbcToHibernateTypeHelper.getJDBCTypeName(dbTypeCode) + " in db") ); + } + } + } + + private void setSchemaSelection(Table table) { + tableSelector.clearSchemaSelections(); + tableSelector.addSchemaSelection( createSchemaSelection( + table.getCatalog(), + table.getSchema(), + table.getName() ) ); + } + + /** + * @return iterator over all the IdentifierGenerator's found in the entitymodel and return a list of unique IdentifierGenerators + * @throws MappingException + */ + private Iterator iterateGenerators() throws MappingException { + + TreeMap generators = + new TreeMap(); + + Iterator persistentClassIterator = getMetadata().getEntityBindings().iterator(); + while ( persistentClassIterator.hasNext() ) { + PersistentClass pc = persistentClassIterator.next(); + + if ( !pc.isInherited() ) { + + Generator ig = pc.getIdentifier() + .createGenerator( + dialect, + (RootClass) pc + ); + + if ( ig instanceof PersistentIdentifierGenerator ) { + generators.put( getGeneratorKey( (PersistentIdentifierGenerator) ig ), ig ); + } + + } + } + + Iterator collectionIterator = getMetadata().getCollectionBindings().iterator(); + while ( collectionIterator.hasNext() ) { + org.hibernate.mapping.Collection collection = (org.hibernate.mapping.Collection) collectionIterator.next(); + + if ( collection.isIdentified() ) { + + Generator ig = ( (IdentifierCollection) collection ).getIdentifier() + .createGenerator( + dialect, + null + ); + + if ( ig instanceof PersistentIdentifierGenerator ) { + generators.put( ( getGeneratorKey((PersistentIdentifierGenerator) ig )), ig ); + } + + } + } + + return generators.values().iterator(); + } + + private Collection
readFromDatabase() { + RevengMetadataCollector revengMetadataCollector = new RevengMetadataCollector(); + reader.readDatabaseSchema(revengMetadataCollector); + return revengMetadataCollector.getTables(); + } + + private SchemaSelection createSchemaSelection(String matchCatalog, String matchSchema, String matchTable) { + return new SchemaSelection() { + @Override + public String getMatchCatalog() { + return matchCatalog; + } + @Override + public String getMatchSchema() { + return matchSchema; + } + @Override + public String getMatchTable() { + return matchTable; + } + + }; + } + + private String getGeneratorKey(PersistentIdentifierGenerator ig) { + String result = null; + if (ig instanceof SequenceStyleGenerator) { + result = getKeyForSequenceStyleGenerator((SequenceStyleGenerator)ig); + } else if (ig instanceof TableGenerator) { + result = getKeyForTableGenerator((TableGenerator)ig); + } + return result; + } + + private String getKeyForSequenceStyleGenerator(SequenceStyleGenerator ig) { + return ig.getDatabaseStructure().getPhysicalName().render(); + } + + private String getKeyForTableGenerator(TableGenerator ig) { + return ig.getTableName(); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/SequenceCollector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/SequenceCollector.java new file mode 100644 index 000000000000..431e027e2b71 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/SequenceCollector.java @@ -0,0 +1,77 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2020-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashSet; +import java.util.Set; + +public class SequenceCollector { + + public static SequenceCollector create(ConnectionProvider provider) { + return new SequenceCollector(provider); + } + + final private ConnectionProvider provider; + + private SequenceCollector(ConnectionProvider provider) { + this.provider = provider; + } + + public Set readSequences(String sql) { + Set sequences = new HashSet(); + if (sql != null) { + Connection connection = null; + try { + + connection = provider.getConnection(); + Statement statement = null; + ResultSet rs = null; + try { + statement = connection.createStatement(); + rs = statement.executeQuery(sql); + while (rs.next()) { + sequences.add(rs.getString("SEQUENCE_NAME").toLowerCase().trim()); + } + } finally { + if (rs != null) + rs.close(); + if (statement != null) + statement.close(); + } + + } catch (SQLException e) { + throw new RuntimeException("Problem while closing connection", e); + } finally { + if (connection != null) + try { + provider.closeConnection(connection); + } catch (SQLException e) { + throw new RuntimeException("Problem while closing connection", e); + } + } + } + return sequences; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/ShadowedIdentifierDetector.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/ShadowedIdentifierDetector.java new file mode 100644 index 000000000000..aaf12c9ad444 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/lint/ShadowedIdentifierDetector.java @@ -0,0 +1,36 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.lint; + +import org.hibernate.mapping.Property; + +public class ShadowedIdentifierDetector extends EntityModelDetector { + + public String getName() { + return "shadow-id"; + } + + @Override + protected void visitProperty(Property property, IssueCollector collector) { + if(property.getName().equals("id")) { + if (property != property.getPersistentClass().getIdentifierProperty()) { + collector.reportIssue(new Issue("ID_SHADOWED", Issue.LOW_PRIORITY, property.getPersistentClass().getEntityName() + " has a normal property named 'id'. This can cause issues since HQL queries will always interpret 'id' as the identifier and not the concrete property")); + } + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/query/QueryExporter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/query/QueryExporter.java new file mode 100644 index 000000000000..3b0be2a61279 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/export/query/QueryExporter.java @@ -0,0 +1,105 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.export.query; + +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.tool.reveng.internal.export.common.AbstractExporter; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.List; + +/** + * exporter for query execution. + * + **/ +public class QueryExporter extends AbstractExporter { + + public void doStart() { + Session session = null; + SessionFactory sessionFactory = null; + Transaction transaction = null; + try { + sessionFactory = buildMetadata().buildSessionFactory(); + session = sessionFactory.openSession(); + transaction = session.beginTransaction(); + for (Iterator iter = getQueryList().iterator(); iter.hasNext();) { + String query = (String) iter.next(); + + List list = session.createQuery(query, null).getResultList(); + + if(getFileName()!=null) { + PrintWriter pw = null; + try { + File file = new File( getOutputDirectory(), getFileName() ); + getTemplateHelper().ensureExistence( file ); + pw = new PrintWriter( new FileWriter( file, true ) ); + getArtifactCollector().addFile( file, "query-output" ); + + for (Iterator iter1 = list.iterator(); iter1.hasNext();) { + Object element = iter1.next(); + pw.println(element); + } + + } + catch (IOException e) { + throw new RuntimeException("Could not write query output",e); + } finally { + if(pw!=null) { + pw.flush(); + pw.close(); + } + } + } + } + transaction.commit(); + } catch(HibernateException he) { + if(transaction!=null) { + transaction.rollback(); + } + throw new RuntimeException("Error occured while trying to execute query", he); + } finally { + if(session!=null) { + session.close(); + } + if(sessionFactory!=null) { + sessionFactory.close(); + } + + } + } + + private String getFileName() { + return (String)getProperties().get(OUTPUT_FILE_NAME); + } + + private List getQueryList() { + return (List)getProperties().get(QUERY_LIST); + } + + public void setQueries(List queryStrings) { + getProperties().put(QUERY_LIST, queryStrings); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/JpaMetadataDescriptor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/JpaMetadataDescriptor.java new file mode 100644 index 000000000000..66a60a32b7e9 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/JpaMetadataDescriptor.java @@ -0,0 +1,77 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2017-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.metadata; + +import jakarta.persistence.EntityManagerFactory; +import org.hibernate.HibernateException; +import org.hibernate.boot.Metadata; +import org.hibernate.jpa.HibernatePersistenceProvider; +import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; +import org.hibernate.tool.reveng.api.metadata.MetadataDescriptor; + +import java.util.Properties; + +public class JpaMetadataDescriptor implements MetadataDescriptor { + + private Properties properties = new Properties(); + private Metadata metadata = null; + + public JpaMetadataDescriptor( + final String persistenceUnit, + final Properties properties) { + EntityManagerFactoryBuilderImpl entityManagerFactoryBuilder = + createEntityManagerFactoryBuilder(persistenceUnit, properties); + EntityManagerFactory entityManagerFactory = + entityManagerFactoryBuilder.build(); + metadata = entityManagerFactoryBuilder.getMetadata(); + properties.putAll(entityManagerFactory.getProperties()); + } + + public Metadata createMetadata() { + return metadata; + } + + public Properties getProperties() { + return properties; + } + + private static class PersistenceProvider extends HibernatePersistenceProvider { + public EntityManagerFactoryBuilderImpl getEntityManagerFactoryBuilder( + String persistenceUnit, + Properties properties) { + EntityManagerFactoryBuilderImpl result = (EntityManagerFactoryBuilderImpl)getEntityManagerFactoryBuilderOrNull( + persistenceUnit, + properties); + if (result == null) { + throw new HibernateException( + "Persistence unit not found: '" + persistenceUnit + "'." + ); + } + return result; + } + } + + private static EntityManagerFactoryBuilderImpl createEntityManagerFactoryBuilder( + final String persistenceUnit, + final Properties properties) { + return new PersistenceProvider().getEntityManagerFactoryBuilder( + persistenceUnit, + properties); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/NativeMetadataDescriptor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/NativeMetadataDescriptor.java new file mode 100644 index 000000000000..ad5105c2d1e6 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/NativeMetadataDescriptor.java @@ -0,0 +1,71 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2017-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.metadata; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.BootstrapServiceRegistry; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.tool.reveng.api.metadata.MetadataDescriptor; + +import java.io.File; +import java.util.Properties; + +public class NativeMetadataDescriptor implements MetadataDescriptor { + + private Properties properties = new Properties(); + private MetadataSources metadataSources = null; + private StandardServiceRegistryBuilder ssrb = null; + + public NativeMetadataDescriptor( + File cfgXmlFile, + File[] mappingFiles, + Properties properties) { + BootstrapServiceRegistry bsr = new BootstrapServiceRegistryBuilder().build(); + ssrb = new StandardServiceRegistryBuilder(bsr); + if (cfgXmlFile != null) { + ssrb.configure(cfgXmlFile); + } + if (properties != null) { + this.properties.putAll(properties); + } + ssrb.applySettings(getProperties()); + metadataSources = new MetadataSources(bsr); + if (mappingFiles != null) { + for (File file : mappingFiles) { + if (file.getName().endsWith(".jar")) { + metadataSources.addJar(file); + } else { + metadataSources.addFile(file); + } + } + } + } + + public Properties getProperties() { + Properties result = new Properties(); + result.putAll(properties); + return result; + } + + public Metadata createMetadata() { + return metadataSources.buildMetadata(ssrb.build()); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/RevengMetadataDescriptor.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/RevengMetadataDescriptor.java new file mode 100644 index 000000000000..b612872b55a1 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/metadata/RevengMetadataDescriptor.java @@ -0,0 +1,64 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2017-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.metadata; + +import org.hibernate.boot.Metadata; +import org.hibernate.cfg.Environment; +import org.hibernate.tool.reveng.api.metadata.MetadataConstants; +import org.hibernate.tool.reveng.api.metadata.MetadataDescriptor; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.RevengStrategyFactory; +import org.hibernate.tool.reveng.internal.core.RevengMetadataBuilder; + +import java.util.Properties; + +public class RevengMetadataDescriptor implements MetadataDescriptor { + + private RevengStrategy reverseEngineeringStrategy = null; + private Properties properties = new Properties(); + + public RevengMetadataDescriptor( + RevengStrategy reverseEngineeringStrategy, + Properties properties) { + this.properties.putAll(Environment.getProperties()); + if (properties != null) { + this.properties.putAll(properties); + } + if (reverseEngineeringStrategy != null) { + this.reverseEngineeringStrategy = reverseEngineeringStrategy; + } else { + this.reverseEngineeringStrategy = RevengStrategyFactory.createReverseEngineeringStrategy(); + } + if (this.properties.get(MetadataConstants.PREFER_BASIC_COMPOSITE_IDS) == null) { + this.properties.put(MetadataConstants.PREFER_BASIC_COMPOSITE_IDS, true); + } + } + + public Properties getProperties() { + Properties result = new Properties(); + result.putAll(properties); + return result; + } + + public Metadata createMetadata() { + return RevengMetadataBuilder + .create(properties, reverseEngineeringStrategy) + .build(); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/stat/StatisticsCellRenderer.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/stat/StatisticsCellRenderer.java new file mode 100644 index 000000000000..b1b07cabfca2 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/stat/StatisticsCellRenderer.java @@ -0,0 +1,71 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.stat; + +import org.hibernate.stat.CollectionStatistics; +import org.hibernate.stat.EntityStatistics; +import org.hibernate.stat.QueryStatistics; +import org.hibernate.stat.Statistics; + +import javax.swing.*; +import javax.swing.tree.DefaultTreeCellRenderer; +import java.awt.*; +import java.text.SimpleDateFormat; +import java.util.Date; + +@SuppressWarnings("serial") +public class StatisticsCellRenderer extends DefaultTreeCellRenderer { + + SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); + + public Component getTreeCellRendererComponent(JTree tree, Object value, + boolean selected, boolean expanded, boolean leaf, int row, + boolean hasFocus) { + + JLabel treeCellRendererComponent = (JLabel) super.getTreeCellRendererComponent( tree, value, selected, expanded, leaf, row, hasFocus ); + + String text = treeCellRendererComponent.getText(); + String tooltip = null; + if(value instanceof Statistics) { + Statistics stats = (Statistics) value; + text = "Statistics " + formatter.format( new Date(stats.getStart().toEpochMilli()) ); + tooltip = stats.toString(); + } + + if(value instanceof EntityStatistics) { + //EntityStatistics stats = (EntityStatistics) value; + + } + + if(value instanceof CollectionStatistics) { + //CollectionStatistics stats = (CollectionStatistics) value; + + } + + if(value instanceof QueryStatistics) { + //QueryStatistics stats = (QueryStatistics) value; + + } + + treeCellRendererComponent.setText( text ); + treeCellRendererComponent.setToolTipText( tooltip ); + return treeCellRendererComponent; + } + + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/stat/StatisticsTreeModel.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/stat/StatisticsTreeModel.java new file mode 100644 index 000000000000..a6794a951f5e --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/stat/StatisticsTreeModel.java @@ -0,0 +1,115 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.stat; + +import org.hibernate.internal.util.collections.IdentityMap; +import org.hibernate.stat.CacheRegionStatistics; +import org.hibernate.stat.Statistics; +import org.hibernate.tool.reveng.internal.util.AbstractTreeModel; + +import java.util.Collections; +import java.util.Map; + +public class StatisticsTreeModel extends AbstractTreeModel { + + private final Statistics stats; + + String queries = "Queries"; + String entities = "Entities"; + String collections = "Collections"; + String secondlevelcache = "Cache"; + + Map im = IdentityMap.instantiateSequenced( 10 ); + + public StatisticsTreeModel(Statistics stats) { + this.stats = stats; + } + + public Object getChild(Object parent, int index) { + if(parent==stats) { + switch(index) { + case 0: return entities; + case 1: return collections; + case 2: return queries; + case 3: return secondlevelcache; + } + } else if(parent==entities) { + return stats.getEntityStatistics(stats.getEntityNames()[index]); + } else if(parent==collections) { + return stats.getCollectionStatistics(stats.getCollectionRoleNames()[index]); + } else if(parent==queries) { + return stats.getQueryStatistics(stats.getQueries()[index]); + } else if(parent==secondlevelcache) { + return stats.getCacheRegionStatistics(stats.getSecondLevelCacheRegionNames()[index]); + } else if (parent instanceof CacheRegionStatistics) { + return Collections.emptyMap(); + } + return null; + } + + public int getChildCount(Object parent) { + if(parent==stats) { + return 4; + } else if(parent==entities) { + return stats.getEntityNames().length; + } else if(parent==collections) { + return stats.getCollectionRoleNames().length; + } else if(parent==queries) { + return stats.getQueries().length; + } else if(parent==secondlevelcache) { + return stats.getSecondLevelCacheRegionNames().length; + } else if(parent instanceof CacheRegionStatistics) { + return 0; + } + return 0; + } + + public int getIndexOfChild(Object parent, Object child) { + throw new IllegalAccessError(); + //return 0; + } + + public Object getRoot() { + return stats; + } + + public boolean isLeaf(Object node) { + return false; + } + + public boolean isQueries(Object o) { + return o==queries; // hack + } + + public boolean isCollections(Object o) { + return o==collections; // hack + } + + public boolean isEntities(Object o) { + return o==entities; // hack + } + + public boolean isCache(Object o) { + return o==secondlevelcache; + } + + public boolean isContainer(Object o) { + return isEntities( o ) || isQueries( o ) || isCollections( o ) || isCache( o ); + } + +} \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/AbstractTreeModel.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/AbstractTreeModel.java new file mode 100644 index 000000000000..abd9aef5f7e5 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/AbstractTreeModel.java @@ -0,0 +1,175 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.util; + +import javax.swing.event.EventListenerList; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +/** + * Abstract class implementing the support for TreeModelEvents + * and TreeModelListeners. The code is partly snipped from the + * implementation of DefaultTreeModel. + * + * @version $Revision: 1.1.2.1 $ + **/ + +public abstract class AbstractTreeModel implements TreeModel { + + private EventListenerList listenerList = new EventListenerList(); + + /** + * Adds a listener for the TreeModelEvent posted after the tree changes. + * + * @see #removeTreeModelListener + * @param l the listener to add + */ + public void addTreeModelListener(TreeModelListener l) { + listenerList.add(TreeModelListener.class, l); + } + + /** + * Removes a listener previously added with addTreeModelListener(). + * + * @see #addTreeModelListener + * @param l the listener to remove + */ + public void removeTreeModelListener(TreeModelListener l) { + listenerList.remove(TreeModelListener.class, l); + } + + /** + * Messaged when the user has altered the value for the item identified + * by path to newValue. If newValue signifies + * a truly new value the model should post a treeNodesChanged + * event. + * + * @param path path to the node that the user has altered. + * @param newValue the new value from the TreeCellEditor. + */ + public void valueForPathChanged(TreePath path, Object newValue) { + throw new RuntimeException("AbstractTreeModel.valueForPathChanged: you MUST override this when using a TreeCellEditor!"); + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeNodesChanged(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeNodesChanged(e); + } + } + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeNodesInserted(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeNodesInserted(e); + } + } + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeNodesRemoved(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e); + } + } + } + + /* + * Notify all listeners that have registered interest for + * notification on this event type. The event instance + * is lazily created using the parameters passed into + * the fire method. + * @see EventListenerList + */ + protected void fireTreeStructureChanged(Object source, Object[] path, + int[] childIndices, + Object[] children) { + // Guaranteed to return a non-null array + Object[] listeners = listenerList.getListenerList(); + TreeModelEvent e = null; + // Process the listeners last to first, notifying + // those that are interested in this event + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==TreeModelListener.class) { + // Lazily create the event: + if (e == null) + e = new TreeModelEvent(source, path, + childIndices, children); + ((TreeModelListener)listeners[i+1]).treeStructureChanged(e); + } + } + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/AnnotationBuilder.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/AnnotationBuilder.java new file mode 100644 index 000000000000..6619272bcff6 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/AnnotationBuilder.java @@ -0,0 +1,160 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.util; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map.Entry; + +public class AnnotationBuilder { + + String annotation; + LinkedHashMap attributes = new LinkedHashMap(); + + public static AnnotationBuilder createAnnotation(String annotation) { + return new AnnotationBuilder(annotation); + } + + protected AnnotationBuilder(String annotation) { + this.annotation = annotation; + } + + public AnnotationBuilder addAttribute(String name, String[] values) { + if(values!=null && values.length > 0) { + attributes.put(name, values); + } + return this; + } + + public AnnotationBuilder addAttribute(String name, String value) { + if(value!=null) { + addAttribute(name, new String[] { value }); + } + return this; + } + + + public AnnotationBuilder resetAnnotation(String annotationName) { + this.annotation = annotationName; + clearAttributes(); + return this; + } + + private AnnotationBuilder clearAttributes() { + attributes.clear(); + return this; + } + + public String getResult() { + StringBuffer b = new StringBuffer("@"); + b.append(annotation); + if(attributes.isEmpty()) { + return b.toString(); + } else { + b.append("("); + Iterator> elements = attributes.entrySet().iterator(); + boolean addedBefore = false; + while ( elements.hasNext() ) { + Entry element = elements.next(); + + String[] s = element.getValue(); + if(s.length==0) { + addedBefore = false; + continue; + } else { + if(addedBefore) { + b.append(", "); + } + String key = element.getKey(); + b.append(key).append("="); + attributeToString( b, s ); + + addedBefore=true; + } + } + b.append( ")" ); + } + return b.toString(); + } + + private void attributeToString(StringBuffer buffer, String[] values) { + if(values.length>1) { + buffer.append( "{" ); + } + + for (int i = 0; i < values.length; i++) { + buffer.append(values[i]); + if(i1) { + buffer.append( "}" ); + } + + } + + public void addQuotedAttributes(String name, Iterator iterator) { + List values = new ArrayList(); + while ( iterator.hasNext() ) { + values.add(quote( iterator.next().toString() )); + } + addAttribute(name, values.toArray( new String[values.size()] )); + } + + public void addAttributes(String name, Iterator iterator) { + List values = new ArrayList(); + while ( iterator.hasNext() ) { + String element = iterator.next().toString(); + values.add( element ); + } + addAttribute(name, values.toArray( new String[values.size()] )); + } + private String quote(String element) { + return "\"" + element + "\""; + } + + public AnnotationBuilder addQuotedAttribute(String name, String value) { + if(value!=null) { + addAttribute(name, quote(value)); + } + return this; + } + + + public String toString() { + return getResult(); + } + + public String getAttributeAsString(String name) { + StringBuffer buffer = new StringBuffer(); + String[] object = (String[]) attributes.get( name ); + if(object==null) { + return null; + } else { + attributeToString( buffer, object ); + return buffer.toString(); + } + } + + +} + diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/BeanTableModel.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/BeanTableModel.java new file mode 100644 index 000000000000..0e77b0d92c8f --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/BeanTableModel.java @@ -0,0 +1,132 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.util; + +import javax.swing.table.AbstractTableModel; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.io.ObjectStreamClass; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +public class BeanTableModel extends AbstractTableModel { + + private static final long serialVersionUID = + ObjectStreamClass.lookup(BeanTableModel.class).getSerialVersionUID(); + + + protected List list; + + private BeanInfo beanInfo = null; + + private PropertyDescriptor[] descriptors = null; + + public BeanTableModel(List list, Class beanClass) { + this.list = list; + introspect( beanClass ); + } + + private void introspect(Class beanClass) { + try { + this.beanInfo = Introspector.getBeanInfo( beanClass, + Introspector.USE_ALL_BEANINFO ); + //descriptor = beanInfo.getBeanDescriptor(); + descriptors = beanInfo.getPropertyDescriptors(); + } + catch (IntrospectionException ie) { + // ignore + } + + List v = new ArrayList(descriptors.length); + for (int i = 0; i < descriptors.length; i++) { + if(!descriptors[i].getName().equals("class")) { + v.add( descriptors[i] ); + } + } + descriptors = (PropertyDescriptor[]) v.toArray( new PropertyDescriptor[v.size()] ); + + } + + boolean isSingle() { + return list.size()<=1; + } + + public int getRowCount() { + return isSingle() ? descriptors.length : list.size(); + } + + public int getColumnCount() { + return isSingle() ? list.size() + 1 : (descriptors != null ? descriptors.length : 0); + } + + public Object getValueAt(int row, int col) { + if(isSingle()) { + if(col==0) { + return descriptors[row].getDisplayName(); + } else { + return getValue(0, row); + } + } else { + return getValue( row, col ); + } + } + + private Object getValue(int row, int col) { + Object bean = list.get( row ); + Object result = null; + try { + result = descriptors[col].getReadMethod().invoke( bean, (Object[])null ); + } + catch (InvocationTargetException ite) { + ite.printStackTrace(); + } + catch (IllegalAccessException iae) { + iae.printStackTrace(); + } + return result; + } + + public String getColumnName(int col) { + if(isSingle()) { + if(col==0) { + return "Name"; + } else { + return "Value"; + } + } else { + return descriptors[col].getDisplayName(); + } + } + + public Class getColumnClass(int c) { + if(isSingle()) { + return String.class; + } else { + Class propertyType = descriptors[c].getPropertyType(); + + if(propertyType.isPrimitive()) { + return String.class; // to avoid jtable complain about null table renderer. + } else { + return propertyType; + } + } + } +} \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/IteratorTransformer.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/IteratorTransformer.java new file mode 100644 index 000000000000..64bbf6326872 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/IteratorTransformer.java @@ -0,0 +1,44 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2015-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.util; + +import java.util.Iterator; + +public abstract class IteratorTransformer implements Iterator { + + private Iterator delegate; + + public IteratorTransformer(Iterator delegate) { + this.delegate = delegate; + } + + public boolean hasNext() { + return delegate.hasNext(); + } + + public String next() { + return transform(delegate.next()); + } + + public abstract String transform(T object); + + public void remove() { + delegate.remove(); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/JdbcToHibernateTypeHelper.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/JdbcToHibernateTypeHelper.java new file mode 100644 index 000000000000..095fdde89abe --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/JdbcToHibernateTypeHelper.java @@ -0,0 +1,224 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.util; + +import org.hibernate.MappingException; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.sql.SQLException; +import java.sql.Types; +import java.util.HashMap; +import java.util.Map; + + +/** + * Utility class for mapping between sqltypes and hibernate type names. + * + * @author max (based on parts from Sql2Java from Middlegen) + * + */ +public final class JdbcToHibernateTypeHelper { + + private JdbcToHibernateTypeHelper() { + + } + + /** The Map containing the preferred conversion type values. */ + private static final Map PREFERRED_HIBERNATETYPE_FOR_SQLTYPE = new HashMap(); + + static { + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.TINYINT), new String[] { "byte", Byte.class.getName()} ); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.SMALLINT), new String[] { "short", Short.class.getName()} ); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.INTEGER), new String[] { "int", Integer.class.getName()} ); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.BIGINT), new String[] { "long", Long.class.getName()} ); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.REAL), new String[] { "float", Float.class.getName()} ); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.FLOAT), new String[] { "double", Double.class.getName()} ); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.DOUBLE), new String[] { "double", Double.class.getName()}); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.DECIMAL), new String[] { "big_decimal", "big_decimal" }); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.NUMERIC), new String[] { "big_decimal", "big_decimal" }); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.BIT), new String[] { "boolean", Boolean.class.getName()}); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.BOOLEAN), new String[] { "boolean", Boolean.class.getName()}); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.CHAR), new String[] { "char", Character.class.getName()}); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.VARCHAR), new String[] { "string", "string" }); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.LONGVARCHAR), new String[] { "string", "string" }); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.BINARY), new String[] { "binary", "binary" }); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.VARBINARY), new String[] { "binary", "binary" }); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.LONGVARBINARY), new String[] { "binary", "binary" }); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.DATE), new String[] { "date", "date" }); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.TIME), new String[] { "time", "time" }); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.TIMESTAMP), new String[] { "timestamp", "timestamp" }); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.CLOB), new String[] { "clob", "clob" }); + PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.put(Integer.valueOf(Types.BLOB), new String[] { "blob", "blob" }); + + //Hibernate does not have any built-in Type for these: + //preferredJavaTypeForSqlType.put(new Integer(Types.ARRAY), "java.sql.Array"); + //preferredJavaTypeForSqlType.put(new Integer(Types.REF), "java.sql.Ref"); + //preferredJavaTypeForSqlType.put(new Integer(Types.STRUCT), "java.lang.Object"); + //preferredJavaTypeForSqlType.put(new Integer(Types.JAVA_OBJECT), "java.lang.Object"); + } + + /* (non-Javadoc) + * @see org.hibernate.cfg.JDBCTypeToHibernateTypesStrategy#getPreferredHibernateType(int, int, int, int) + */ + public static String getPreferredHibernateType(int sqlType, int size, int precision, int scale, boolean nullable, boolean generatedIdentifier) { + boolean returnNullable = nullable || generatedIdentifier; + if ( (sqlType == Types.DECIMAL || sqlType == Types.NUMERIC) && scale <= 0) { // <= + if (precision == 1) { + // NUMERIC(1) is a often used idiom for storing boolean thus providing it out of the box. + return returnNullable?Boolean.class.getName():"boolean"; + } + else if (precision < 3) { + return returnNullable?Byte.class.getName():"byte"; + } + else if (precision < 5) { + return returnNullable?Short.class.getName():"short"; + } + else if (precision < 10) { + return returnNullable?Integer.class.getName():"int"; + } + else if (precision < 19) { + return returnNullable?Long.class.getName():"long"; + } + else { + return "big_integer"; + } + } + + if ( sqlType == Types.CHAR && size>1 ) { + return "string"; + } + + String[] result = (String[]) PREFERRED_HIBERNATETYPE_FOR_SQLTYPE.get(Integer.valueOf(sqlType) ); + + if(result==null) { + return null; + } else if(returnNullable) { + return result[1]; + } else { + return result[0]; + } + } + + static Map jdbcTypes; // Name to value + static Map jdbcTypeValues; // value to Name + + public static String[] getJDBCTypes() { + checkTypes(); + + return (String[]) jdbcTypes.keySet().toArray(new String[jdbcTypes.size()]); + } + + public static int getJDBCType(String value) { + checkTypes(); + + Integer number = (Integer) jdbcTypes.get(value); + + if(number==null) { + try { + return Integer.parseInt(value); + } + catch (NumberFormatException nfe) { + throw new MappingException("jdbc-type: " + value + " is not a known JDBC Type nor a valid number"); + } + } + else { + return number.intValue(); + } + } + + private static void checkTypes() { + if(jdbcTypes==null) { + jdbcTypes = new HashMap(); + Field[] fields = Types.class.getFields(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + if(Modifier.isStatic(field.getModifiers() ) ) { + try { + jdbcTypes.put(field.getName(), (Integer)field.get(Types.class) ); + } + catch (IllegalArgumentException e) { + // ignore + } + catch (IllegalAccessException e) { + // ignore + } + } + } + } + } + + public static String getJDBCTypeName(int value) { + if(jdbcTypeValues==null) { + jdbcTypeValues = new HashMap(); + Field[] fields = Types.class.getFields(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + if(Modifier.isStatic(field.getModifiers() ) ) { + try { + jdbcTypeValues.put((Integer)field.get(Types.class), field.getName() ); + } + catch (IllegalArgumentException e) { + // ignore + } + catch (IllegalAccessException e) { + // ignore + } + } + } + } + + String name = (String) jdbcTypeValues.get(Integer.valueOf(value) ); + + if(name!=null) { + return name; + } + else { + return ""+value; + } + } + + /** + * @param table + * @param schema + * @param catalog + * @throws SQLException + */ + + // scale is for non floating point numeric columns + public static boolean typeHasScale(int sqlType) { + return (sqlType == Types.DECIMAL || sqlType == Types.NUMERIC); + } + + // precision is for numeric columns + public static boolean typeHasPrecision(int sqlType) { + return (sqlType == Types.DECIMAL || sqlType == Types.NUMERIC + || sqlType == Types.REAL || sqlType == Types.FLOAT || sqlType == Types.DOUBLE); + } + + public static boolean typeHasScaleAndPrecision(int sqlType) { + return typeHasScale(sqlType) && typeHasPrecision(sqlType); + } + + // length is for string columns + public static boolean typeHasLength(int sqlType) { + return (sqlType == Types.CHAR || sqlType == Types.DATE + || sqlType == Types.LONGVARCHAR || sqlType == Types.TIME || sqlType == Types.TIMESTAMP + || sqlType == Types.VARCHAR ); + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/NameConverter.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/NameConverter.java new file mode 100644 index 000000000000..44b6594aae53 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/NameConverter.java @@ -0,0 +1,163 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.util; + +import java.util.HashSet; +import java.util.Set; + +final public class NameConverter { + + private static final Set RESERVED_KEYWORDS; + static { + RESERVED_KEYWORDS = new HashSet<>(); + + RESERVED_KEYWORDS.add( "abstract" ); + RESERVED_KEYWORDS.add( "continue" ); + RESERVED_KEYWORDS.add( "for" ); + RESERVED_KEYWORDS.add( "new" ); + RESERVED_KEYWORDS.add( "switch" ); + RESERVED_KEYWORDS.add( "assert" ); + RESERVED_KEYWORDS.add( "default" ); + RESERVED_KEYWORDS.add( "goto" ); + RESERVED_KEYWORDS.add( "package" ); + RESERVED_KEYWORDS.add( "synchronized" ); + RESERVED_KEYWORDS.add( "boolean" ); + RESERVED_KEYWORDS.add( "do" ); + RESERVED_KEYWORDS.add( "if" ); + RESERVED_KEYWORDS.add( "private" ); + RESERVED_KEYWORDS.add( "this" ); + RESERVED_KEYWORDS.add( "break" ); + RESERVED_KEYWORDS.add( "double" ); + RESERVED_KEYWORDS.add( "implements" ); + RESERVED_KEYWORDS.add( "protected" ); + RESERVED_KEYWORDS.add( "throw" ); + RESERVED_KEYWORDS.add( "byte" ); + RESERVED_KEYWORDS.add( "else" ); + RESERVED_KEYWORDS.add( "import" ); + RESERVED_KEYWORDS.add( "public" ); + RESERVED_KEYWORDS.add( "throws" ); + RESERVED_KEYWORDS.add( "case" ); + RESERVED_KEYWORDS.add( "enum" ); + RESERVED_KEYWORDS.add( "instanceof" ); + RESERVED_KEYWORDS.add( "return" ); + RESERVED_KEYWORDS.add( "transient" ); + RESERVED_KEYWORDS.add( "catch" ); + RESERVED_KEYWORDS.add( "extends" ); + RESERVED_KEYWORDS.add( "int" ); + RESERVED_KEYWORDS.add( "short" ); + RESERVED_KEYWORDS.add( "try" ); + RESERVED_KEYWORDS.add( "char" ); + RESERVED_KEYWORDS.add( "final" ); + RESERVED_KEYWORDS.add( "interface" ); + RESERVED_KEYWORDS.add( "static" ); + RESERVED_KEYWORDS.add( "void" ); + RESERVED_KEYWORDS.add( "class" ); + RESERVED_KEYWORDS.add( "finally" ); + RESERVED_KEYWORDS.add( "long" ); + RESERVED_KEYWORDS.add( "strictfp" ); + RESERVED_KEYWORDS.add( "volatile" ); + RESERVED_KEYWORDS.add( "const" ); + RESERVED_KEYWORDS.add( "float" ); + RESERVED_KEYWORDS.add( "native" ); + RESERVED_KEYWORDS.add( "super" ); + RESERVED_KEYWORDS.add( "while" ); + } + + private NameConverter() {} + + /** + * Converts a database name (table or column) to a java name (first letter capitalised). + * employee_name -> EmployeeName. + *

+ * Derived from middlegen's dbnameconverter. + * @param s The database name to convert. + * + * @return The converted database name. + */ + public static String toUpperCamelCase(String s) { + if (s.isEmpty()) { + return s; + } + StringBuilder result = new StringBuilder(); + + boolean capitalize = true; + boolean lastCapital = false; + boolean lastDecapitalized = false; + for (int i = 0; i < s.length(); i++) { + String c = s.substring(i, i + 1); + if ( "_".equals(c) || " ".equals(c) || "-".equals(c) ) { + capitalize = true; + continue; + } + + if ( c.toUpperCase().equals(c) ) { + if (lastDecapitalized && !lastCapital) { + capitalize = true; + } + lastCapital = true; + } + else { + lastCapital = false; + } + + //if(forceFirstLetter && result.length()==0) capitalize = false; + + if (capitalize) { + result.append(c.toUpperCase()); + capitalize = false; + } + else { + result.append(c.toLowerCase() ); + lastDecapitalized = true; + } + + } + return result.toString(); + } + + static public String simplePluralize(String singular) { + char last = singular.charAt( singular.length()-1 ); + Character prev = singular.length() > 1 ? singular.charAt( singular.length() - 2 ) : null; + String vowels = "aeiouy"; + switch (last) { + case 'x': + case 's': + singular += "es"; + break; + case 'y': + if (prev != null && vowels.indexOf(prev) >= 0){ + singular += "s"; + } else { + singular = singular.substring( 0, singular.length()-1 ) + "ies"; + } + break; + case 'h': + if (prev != null && (prev == 'c' || prev == 's')){ + singular += "es"; + break; + } + default: + singular += "s"; + } + return singular; + } + + static public boolean isReservedJavaKeyword(String str) { + return RESERVED_KEYWORDS.contains(str); + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/ReflectionUtil.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/ReflectionUtil.java new file mode 100644 index 000000000000..84484d77ac0e --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/ReflectionUtil.java @@ -0,0 +1,33 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2004-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.util; + +public class ReflectionUtil { + + public static Class classForName(String name) throws ClassNotFoundException { + try { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if ( classLoader != null ) { + return classLoader.loadClass(name); + } + } + catch ( Throwable ignore ) {} + return Class.forName( name ); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/SkipBackRefPropertyIterator.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/SkipBackRefPropertyIterator.java new file mode 100644 index 000000000000..9ec6f071d9f5 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/SkipBackRefPropertyIterator.java @@ -0,0 +1,77 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.util; + +import org.hibernate.mapping.Property; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Helper iterator to ignore "backrefs" properties in hibernate mapping model. + * + * @author Max Rydahl Andersen + * + */ +public class SkipBackRefPropertyIterator implements Iterator { + + private Iterator delegate; + + private Property backLog; + + public SkipBackRefPropertyIterator(Iterator iterator) { + delegate = iterator; + } + + public boolean hasNext() { + if ( backLog!=null ) { + return true; + } else if ( delegate.hasNext() ) { + Property nextProperty = (Property) delegate.next(); + while ( nextProperty.isBackRef() && delegate.hasNext() ) { + nextProperty = (Property) delegate.next(); + } + if ( !nextProperty.isBackRef() ) { + backLog = nextProperty; + return true; + } + } + return false; + } + + public Property next() { + if ( backLog != null ) { + Property p = backLog; + backLog = null; + return p; + } + Property nextProperty = (Property) delegate.next(); + while ( nextProperty.isBackRef() && delegate.hasNext() ) { + nextProperty = (Property) delegate.next(); + } + if ( nextProperty.isBackRef() ) { + throw new NoSuchElementException(); + } + return nextProperty; + } + + public void remove() { + throw new UnsupportedOperationException( "remove() not allowed" ); + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/StringUtil.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/StringUtil.java new file mode 100644 index 000000000000..f46e3ef90486 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/util/StringUtil.java @@ -0,0 +1,83 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2018-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.util; + +import java.util.Objects; +import java.util.StringTokenizer; + +/** + * max: Removed methods that dependent on anything else than common.StringUtils. + * + *

Common String manipulation routines.

+ * + *

Originally from + * Turbine and the + * GenerationJavaCore library.

+ * + * @author Jon S. Stevens + * @author Daniel Rall + * @author Greg Coladonato + * @author Henri Yandell + * @author Ed Korthof + * @author Stephen Colebourne + * @author getIdentifierGeneratorParameters() { + getBuildingContext().getMetadataCollector().getIdentifierGenerator("foo"); + return Collections.emptyMap(); + } + + public String getIdentifierGeneratorStrategy() { + return null; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/AbstractXMLPrettyPrinterStrategy.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/AbstractXMLPrettyPrinterStrategy.java new file mode 100644 index 000000000000..d0b2402cf501 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/AbstractXMLPrettyPrinterStrategy.java @@ -0,0 +1,80 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2017-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.xml; + +import org.hibernate.tool.reveng.api.xml.XMLPrettyPrinterStrategy; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.logging.Logger; + +public abstract class AbstractXMLPrettyPrinterStrategy implements XMLPrettyPrinterStrategy { + + private static final Logger LOGGER = Logger.getLogger( AbstractXMLPrettyPrinterStrategy.class.getName() ); + private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = createDocumentBuilderFactory(); + + public Document newDocument(String xml, String encoding) throws SAXException, IOException, ParserConfigurationException { + final Document document = DOCUMENT_BUILDER_FACTORY + .newDocumentBuilder() + .parse(new InputSource(new ByteArrayInputStream(xml.getBytes(encoding)))); + document.normalize(); + return document; + } + + protected void removeWhitespace(final Document document) throws XPathExpressionException { + XPath xPath = XPathFactory.newInstance().newXPath(); + NodeList nodeList = (NodeList) xPath.evaluate("//text()[normalize-space()='']", + document, + XPathConstants.NODESET); + + for (int i = 0; i < nodeList.getLength(); ++i) { + Node node = nodeList.item(i); + node.getParentNode().removeChild(node); + } + } + + private static DocumentBuilderFactory createDocumentBuilderFactory() { + DocumentBuilderFactory result = null; + try { + result = DocumentBuilderFactory.newInstance( + "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", + null); + result.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + } + catch (ParserConfigurationException e) { + LOGGER.severe( + "A ParserConfigurationException happened while setting the " + + "'http://apache.org/xml/features/nonvalidating/load-external-dtd' feature" + + "to false." ); + throw new RuntimeException(e); + } + return result; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/DOM3LSPrettyPrinterStrategy.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/DOM3LSPrettyPrinterStrategy.java new file mode 100644 index 000000000000..36ef65669e82 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/DOM3LSPrettyPrinterStrategy.java @@ -0,0 +1,81 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2017-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.xml; + +import org.w3c.dom.DOMConfiguration; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; + +import java.io.StringWriter; + +public class DOM3LSPrettyPrinterStrategy extends AbstractXMLPrettyPrinterStrategy { + private boolean outputComments; + + @Override + public String prettyPrint(String xml) throws Exception { + final Document document = newDocument(xml, "UTF-8"); + final DOMImplementationLS domImplementationLS = getDomImplementationLS(document); + final LSSerializer lsSerializer = newLSSerializer(domImplementationLS); + final LSOutput lsOutput = newLSOutput(domImplementationLS); + + final StringWriter stringWriter = new StringWriter(); + lsOutput.setCharacterStream(stringWriter); + lsSerializer.write(document, lsOutput); + return stringWriter.toString(); + } + + protected DOMImplementationLS getDomImplementationLS(final Document document) { + final DOMImplementation domImplementation = document.getImplementation(); + if (domImplementation.hasFeature("LS", "3.0") && domImplementation.hasFeature("Core", "2.0")) { + return (DOMImplementationLS) domImplementation.getFeature("LS", "3.0"); + } else { + throw new RuntimeException("DOM 3.0 LS and/or DOM 2.0 Core not supported."); + } + } + + protected LSSerializer newLSSerializer(final DOMImplementationLS domImplementationLS) { + final LSSerializer lsSerializer = domImplementationLS.createLSSerializer(); + final DOMConfiguration domConfiguration = lsSerializer.getDomConfig(); + if (domConfiguration.canSetParameter("format-pretty-print", Boolean.TRUE)) { + lsSerializer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); + if (domConfiguration.canSetParameter("comments", isOutputComments())) { + lsSerializer.getDomConfig().setParameter("comments", isOutputComments()); + } + return lsSerializer; + } else { + throw new RuntimeException("DOMConfiguration 'format-pretty-print' parameter isn't settable."); + } + } + + protected LSOutput newLSOutput(DOMImplementationLS domImplementationLS) { + final LSOutput lsOutput = domImplementationLS.createLSOutput(); + lsOutput.setEncoding("UTF-8"); + return lsOutput; + } + + public boolean isOutputComments() { + return outputComments; + } + + public void setOutputComments(boolean outputComments) { + this.outputComments = outputComments; + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/TrAXPrettyPrinterStrategy.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/TrAXPrettyPrinterStrategy.java new file mode 100644 index 000000000000..3450d8484d3b --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/TrAXPrettyPrinterStrategy.java @@ -0,0 +1,94 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2017-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.xml; + +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.StringWriter; +import java.util.logging.Logger; + +public class TrAXPrettyPrinterStrategy extends AbstractXMLPrettyPrinterStrategy { + + private static final Logger LOGGER = Logger.getLogger( TrAXPrettyPrinterStrategy.class.getName() ); + private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance( + "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl", + null); + + private int indent = 4; + private boolean omitXmlDeclaration; + + @Override + public String prettyPrint(String xml) throws Exception { + final Document document = newDocument(xml, "UTF-8"); + removeWhitespace(document); + + final Transformer transformer = newTransformer(document); + + final StringWriter stringWriter = new StringWriter(); + transformer.transform(new DOMSource(document), new StreamResult(stringWriter)); + return stringWriter.toString(); + } + + protected Transformer newTransformer(final Document document) throws TransformerConfigurationException { + + final Transformer transformer = TRANSFORMER_FACTORY.newTransformer(); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, isOmitXmlDeclaration() ? "yes" : "no"); + try { + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(getIndent())); + } + catch (IllegalArgumentException e) { + LOGGER.severe( "An IllegalArgumentException happened while adding the 'indent' property." ); + throw new RuntimeException(e); + } + + final DocumentType doctype = document.getDoctype(); + if (doctype != null) { + transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, doctype.getPublicId()); + transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doctype.getSystemId()); + } + + return transformer; + } + + public int getIndent() { + return indent; + } + + public void setIndent(int indent) { + this.indent = indent; + } + + public boolean isOmitXmlDeclaration() { + return omitXmlDeclaration; + } + + public void setOmitXmlDeclaration(boolean omitXmlDeclaration) { + this.omitXmlDeclaration = omitXmlDeclaration; + } + +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/XMLPrettyPrinterStrategyFactory.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/XMLPrettyPrinterStrategyFactory.java new file mode 100644 index 000000000000..5f233fa526b0 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/reveng/internal/xml/XMLPrettyPrinterStrategyFactory.java @@ -0,0 +1,52 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2017-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.xml; + +import org.hibernate.tool.reveng.api.xml.XMLPrettyPrinterStrategy; + +import java.lang.reflect.Constructor; + +public final class XMLPrettyPrinterStrategyFactory { + public static final String PROPERTY_STRATEGY_IMPL = "org.hibernate.tool.hbm2x.xml.XMLPrettyPrinterStrategy"; + + private static final XMLPrettyPrinterStrategy DEFAULT_STRATEGY = new TrAXPrettyPrinterStrategy(); + + private XMLPrettyPrinterStrategyFactory() { + } + + public static XMLPrettyPrinterStrategy newXMLPrettyPrinterStrategy() { + XMLPrettyPrinterStrategy strategy = loadFromSystemProperty(); + return strategy == null ? DEFAULT_STRATEGY : strategy; + } + + @SuppressWarnings("unchecked") + private static XMLPrettyPrinterStrategy loadFromSystemProperty() { + String strategyClass = System.getProperty(PROPERTY_STRATEGY_IMPL); + + if (strategyClass != null) { + try { + Class clazz = (Class) Class.forName(strategyClass); + Constructor constructor = clazz.getConstructor(new Class[] {}); + return constructor.newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return null; + } +} diff --git a/tooling/hibernate-reveng/src/main/resources/dao/daohome.ftl b/tooling/hibernate-reveng/src/main/resources/dao/daohome.ftl new file mode 100644 index 000000000000..050a777ef157 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/dao/daohome.ftl @@ -0,0 +1,279 @@ +<#-- + ~ Copyright 2004 - 2025 Red Hat, Inc. + ~ + ~ 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. + --> +${pojo.getPackageDeclaration()} +// Generated ${date} by Hibernate Tools ${version} + +<#assign classbody> +<#assign declarationName = pojo.importType(pojo.getDeclarationName())>/** + * Home object for domain model class ${declarationName}. + * @see ${pojo.getQualifiedDeclarationName()} + * @author Hibernate Tools + */ +<#if ejb3> +@${pojo.importType("javax.ejb.Stateless")} + +public class ${declarationName}Home { + + private static final ${pojo.importType("java.util.logging.Logger")} logger = ${pojo.importType("Logger")}.getLogger(${pojo.getDeclarationName()}Home.class.getName()); + +<#if ejb3> + @${pojo.importType("jakarta.persistence.PersistenceContext")} private ${pojo.importType("jakarta.persistence.EntityManager")} entityManager; + + public void persist(${declarationName} transientInstance) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "persisting ${declarationName} instance"); + try { + entityManager.persist(transientInstance); + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "persist successful"); + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "persist failed", re); + throw re; + } + } + + public void remove(${declarationName} persistentInstance) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "removing ${declarationName} instance"); + try { + entityManager.remove(persistentInstance); + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "remove successful"); + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "remove failed", re); + throw re; + } + } + + public ${declarationName} merge(${declarationName} detachedInstance) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "merging ${declarationName} instance"); + try { + ${declarationName} result = entityManager.merge(detachedInstance); + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "merge successful"); + return result; + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "merge failed", re); + throw re; + } + } + +<#if clazz.identifierProperty?has_content> + public ${declarationName} findById( ${pojo.getJavaTypeName(clazz.identifierProperty, jdk5)} id) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "getting ${declarationName} instance with id: " + id); + try { + ${declarationName} instance = entityManager.find(${pojo.getDeclarationName()}.class, id); + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "get successful"); + return instance; + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "get failed", re); + throw re; + } + } + +<#else> + private final ${pojo.importType("org.hibernate.SessionFactory")} sessionFactory = getSessionFactory(); + + protected ${pojo.importType("org.hibernate.SessionFactory")} getSessionFactory() { + try { + return (${pojo.importType("org.hibernate.SessionFactory")}) new ${pojo.importType("javax.naming.InitialContext")}().lookup("${sessionFactoryName}"); + } + catch (Exception e) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "Could not locate SessionFactory in JNDI", e); + throw new IllegalStateException("Could not locate SessionFactory in JNDI"); + } + } + + public void persist(${declarationName} transientInstance) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "persisting ${declarationName} instance"); + try { + sessionFactory.getCurrentSession().persist(transientInstance); + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "persist successful"); + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "persist failed", re); + throw re; + } + } + + public void attachDirty(${declarationName} instance) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "attaching dirty ${declarationName} instance"); + try { + sessionFactory.getCurrentSession().merge(instance); + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "attach successful"); + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "attach failed", re); + throw re; + } + } + + public void attachClean(${declarationName} instance) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "attaching clean ${declarationName} instance"); + try { + sessionFactory.getCurrentSession().lock(instance, ${pojo.importType("org.hibernate.LockMode")}.NONE); + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "attach successful"); + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "attach failed", re); + throw re; + } + } + + public void remove(${declarationName} persistentInstance) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "removing ${declarationName} instance"); + try { + sessionFactory.getCurrentSession().remove(persistentInstance); + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "remove successful"); + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "remove failed", re); + throw re; + } + } + + public ${declarationName} merge(${declarationName} detachedInstance) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "merging ${declarationName} instance"); + try { + ${declarationName} result = (${declarationName}) sessionFactory.getCurrentSession() + .merge(detachedInstance); + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "merge successful"); + return result; + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "merge failed", re); + throw re; + } + } + +<#if clazz.identifierProperty?has_content> + public ${declarationName} findById( ${c2j.getJavaTypeName(clazz.identifierProperty, jdk5)} id) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "getting ${declarationName} instance with id: " + id); + try { + ${declarationName} instance = (${declarationName}) sessionFactory.getCurrentSession() + .get("${clazz.entityName}", id); + if (instance==null) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "get successful, no instance found"); + } + else { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "get successful, instance found"); + } + return instance; + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "get failed", re); + throw re; + } + } + + +<#if clazz.hasNaturalId()> + public ${declarationName} findByNaturalId(${c2j.asNaturalIdParameterList(clazz)}) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "getting ${declarationName} instance by natural id"); + try { + ${pojo.importType("jakarta.persistence.criteria.CriteriaBuilder")} criteriaBuilder = sessionFactory.getCriteriaBuilder(); + ${pojo.importType("jakarta.persistence.criteria.CriteriaQuery")}<${declarationName}> criteriaQuery = criteriaBuilder.createQuery(${declarationName}.class); + ${pojo.importType("jakarta.persistence.criteria.Root")}<${declarationName}> root = criteriaQuery.from(${declarationName}.class); + criteriaQuery.where( +<#assign notFirst = false/> +<#list pojo.getAllPropertiesIterator() as property> +<#if property.isNaturalIdentifier()> + <#if notFirst>,criteriaBuilder.equal(root.get("${property.name}"), ${property.name}) +<#assign notFirst = true/> + + + ); + ${declarationName} instance = sessionFactory + .getCurrentSession() + .createQuery(criteriaQuery) + .getSingleResult(); + if (instance==null) { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "get successful, no instance found"); + } + else { + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "get successful, instance found"); + } + return instance; + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "query failed", re); + throw re; + } + } + + +<#if false> +/** +TODO: +<#if jdk5> + public ${pojo.importType("java.util.List")}<${declarationName}> findByExample(${declarationName} instance) { +<#else> + public ${pojo.importType("java.util.List")} findByExample(${declarationName} instance) { + + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "finding ${declarationName} instance by example"); + try { +<#if jdk5> + ${pojo.importType("java.util.List")}<${declarationName}> results = (List<${declarationName}>) sessionFactory.getCurrentSession() +<#else> + ${pojo.importType("java.util.List")} results = sessionFactory.getCurrentSession() + + .createCriteria("${clazz.entityName}") +<#if jdk5> + .add( ${pojo.staticImport("org.hibernate.criterion.Example", "create")}(instance) ) +<#else> + .add(${pojo.importType("org.hibernate.criterion.Example")}.create(instance)) + + .list(); + logger.log(${pojo.importType("java.util.logging.Level")}.INFO, "find by example successful, result size: " + results.size()); + return results; + } + catch (RuntimeException re) { + logger.log(${pojo.importType("java.util.logging.Level")}.SEVERE, "find by example failed", re); + throw re; + } + } +**/ + + +<#list daoHelper.getNamedHqlQueryDefinitions(md) as query> +<#assign queryName = query.registrationName> +<#if queryName.startsWith(clazz.entityName + ".")> +<#assign methname = c2j.unqualify(queryName)> +<#assign params = c2j.getParameterTypes(query)> +<#assign argList = c2j.asFinderArgumentList(params, pojo)> +<#if jdk5 && methname.startsWith("find")> + public ${pojo.importType("java.util.List")}<${declarationName}> ${methname}(${argList}) { +<#elseif methname.startsWith("count")> + public int ${methname}(${argList}) { +<#else> + public ${pojo.importType("java.util.List")} ${methname}(${argList}) { + + ${pojo.importType("org.hibernate.query.Query")} query = sessionFactory.getCurrentSession() + .getNamedQuery("${queryName}"); +<#if jdk5 && methname.startsWith("find")> + return (List<${declarationName}>) query.list(); +<#elseif methname.startsWith("count")> + return ( (Integer) query.uniqueResult() ).intValue(); +<#else> + return query.list(); + + } + + +} + + +${pojo.generateImports()} +${classbody} diff --git a/tooling/hibernate-reveng/src/main/resources/debug.ftl b/tooling/hibernate-reveng/src/main/resources/debug.ftl new file mode 100644 index 000000000000..953d52779068 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/debug.ftl @@ -0,0 +1,34 @@ +<#-- + ~ Copyright 2004 - 2025 Red Hat, Inc. + ~ + ~ 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. + --> +<#macro dump var> + <#if var?is_hash> + { + <#list var?keys as key> + ${key}:< dump var[key]/>, + + } + <#elseif var?is_sequence> + [ + <#list sequence as elem> + < dump elem/>, + + ] + <#else> + ${var} + + + +<@dump .vars/> diff --git a/tooling/hibernate-reveng/src/main/resources/doc/common.ftl b/tooling/hibernate-reveng/src/main/resources/doc/common.ftl new file mode 100644 index 000000000000..e92a68b36c7b --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/common.ftl @@ -0,0 +1,50 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#macro header selected=""> + + + +<#macro headerItem id label href selected> + <#-- unfortunately whitespace is significant here in browsers.. --> + <#if selected == id><#-- + -->
  • <#-- + -->${label}<#-- + -->
  • <#-- + --><#else><#-- + -->
  • <#-- + --><#-- + -->${label}<#-- + --><#-- + -->
  • <#-- + --> + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/doc/doc-style.css b/tooling/hibernate-reveng/src/main/resources/doc/doc-style.css new file mode 100644 index 000000000000..b85c3504e9e4 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/doc-style.css @@ -0,0 +1,213 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2010-2025 Red Hat, Inc. + * + * 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. + */ + +/* + * Hibernate Mapping Documentation Style Sheet + */ + +body +{ + margin: 0.5em; + background: white; + font-family: Arial, Helvetica, sans-serif; + font-size: 10pt; +} + +a +{ + color: #003399; +} + +a:active +{ + color: #003399; +} + +a:visited +{ + color: #888888; +} + +p, ul, hr +{ + margin: 0.5em 0; +} +* html hr /* ie6 */ +{ + margin: 0; +} + +h1 +{ + text-align: center; +} + +table +{ + margin: 0.5em 0; + width: 100%; + border-collapse: collapse; +} +* html table /* ie6 only */ +{ + margin: 1em 0; +} + +th, td +{ + border: solid 1px #CCCCCC; + padding: 3px; +} + +th +{ + background: #F4F4F4; + text-align: left; +} + +td a +{ + font-weight: bold; +} + +hr +{ + border: none; + border-top: 1px solid #CCCCCC; +} +* html hr /* ie6 only */ +{ + height: 1px; +} + +.MainTableHeading +{ + font-size: 20px; + font-weight: bold; +} + +p.MainTableHeading +{ + background: #F4F4F4; + border: solid 1px #CCCCCC; + padding: 3px; +} + +.List p +{ + margin: 0 0 10px 0; +} + +.ListTitleFont +{ + font-size: large; + font-weight: bold; + white-space: nowrap; +} + +/* header */ + +#logo +{ + float: right; + margin-bottom: 0.5em; +} +* html #logo /* ie6 */ +{ + margin-bottom: 0; +} + +#logo img +{ + display: block; + border: none; +} + +#header ul +{ + list-style: none; + background: #F4F4F4; + padding: 4px; + margin: 0; +} + +#header li +{ + display: inline; + margin-right: 3px; + padding: 0 3px; +} + +#header li.selected +{ + background: silver; + color: white; + font-weight: bold; +} + +#header a +{ + color: black; + font-weight: bold; +} + +#header hr +{ + clear: right; +} + +/* entity hierarchy */ + +ul.EntityHierarchy +{ + border: solid 1px #CCCCCC; + background: #F4F4F4; + list-style: none; + padding: 5px; + font: bold 90% monospace; +} +* html ul.EntityHierarchy /* ie6 only */ +{ + margin-left: 0; +} + +.EntityHierarchy a +{ + font-weight: normal; +} + +.EntityHierarchy ul +{ + margin: 0; + list-style: none; + padding-left: 0; +} +* html .EntityHierarchy ul /* ie6 only */ +{ + margin-left: 0; +} + +.EntityHierarchy ul.first +{ + padding-left: 15px; +} + +.EntityHierarchy ul ul +{ + padding-left: 30px; +} diff --git a/tooling/hibernate-reveng/src/main/resources/doc/entities/allEntity-list.ftl b/tooling/hibernate-reveng/src/main/resources/doc/entities/allEntity-list.ftl new file mode 100644 index 000000000000..79722e59eadf --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/entities/allEntity-list.ftl @@ -0,0 +1,36 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + + + Hibernate Mappings - Entity List + + + + +

    + All Entities +

    + +

    + <#list classList as class> + ${class.declarationName}
    + +

    + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/entities/entity.ftl b/tooling/hibernate-reveng/src/main/resources/doc/entities/entity.ftl new file mode 100644 index 000000000000..60664d74a265 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/entities/entity.ftl @@ -0,0 +1,436 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#import "/doc/common.ftl" as common> + + + + + + Hibernate Mappings - Entity Info + + + + + <@common.header/> + +

    + <#if class.packageName?length gt 0> + ${class.packageName} + <#else> + Default package + +

    +

    Entity ${class.getShortName()}

    + +
      +
    • + <#assign superClasses=dochelper.getInheritanceHierarchy(class)> + <#list superClasses?reverse as superClass> + <#-- whitespace is significant here --> + <#if superClass_index gt 0>extended by${superClass.qualifiedDeclarationName} +
        class="first"> +
      • + + <#-- whitespace is significant here --> + <#if class.subclass>extended by${class.qualifiedDeclarationName} + <#list superClasses as superClass> +
      • +
      + +
    • +
    + +
    + + <#if class.getMetaAsString("class-description")?has_content> +

    + ${class.getMetaAsString("class-description")} +

    +
    + + + <#if class.hasIdentifierProperty()> + <#assign propertyIdentifier = class.getIdentifierProperty()> + +
    + + + + + + + + + + + + + + <#if dochelper.getComponentPOJO(propertyIdentifier)?exists> + <#assign compoclass = dochelper.getComponentPOJO(propertyIdentifier)> + <#list compoclass.allPropertiesIterator as property> + <#assign columnIterator = property.getValue().selectables.iterator()> + <#assign rowspan = property.getValue().getColumnSpan()> + + + + <#if (rowspan > 0)> + <#assign column = columnIterator.next()> + <#if column.isFormula()> + + <#else> + + + <#else> + + + + + + + + + <#if (rowspan > 1)> + <#list columnIterator as column> + + + + + + + <#else> + + + + + + + + +
    + Identifier Summary +
    + Name + + Column + + Type + + Description +
    0)>rowspan="${rowspan}"> + + ${property.name} + + +   + + + ${column.getName()} + + +   + 0)>rowspan="${rowspan}"> + <#if dochelper.getComponentPOJO(property)?exists> + + ${compoclass.getJavaTypeName(property, jdk5)?html?default(" ")} + + <#else> + ${compoclass.getJavaTypeName(property, jdk5)?html?default(" ")} + + 0)>rowspan="${rowspan}"> + <#if compoclass.hasFieldJavaDoc(property)?exists> + ${compoclass.getFieldDescription(property)?default(" ")} + <#else> +   + +
    + + ${column.name} + +
    + + ${propertyIdentifier.name} + + + Column + + ${class.getJavaTypeName(propertyIdentifier, jdk5)?html?default(" ")} + + <#if class.hasFieldJavaDoc(propertyIdentifier)> + ${class.getFieldDescription(propertyIdentifier)?default(" ")} + <#else> +   + +
    + <#else> + <#list superClasses as superClass> + <#if superClass.hasIdentifierProperty()> + <#assign identifier = superClass.identifierProperty> + <#assign superClassRef = docFileManager.getRef(docFile, docFileManager.getEntityDocFileByDeclarationName(superClass))> +

    + Identifier Summary +

    + + + + + + + +
    + Identifier inherited from entity ${superClass.shortName} +
    + ${identifier.name} +
    + + + + + <#if class.hasVersionProperty()> + <#assign version = class.versionProperty> + + + + + + + + + + + + + + + + + + + + + + +
    + Version Summary +
    + Name + + Column + + Type + + Description +
    + + ${version.name} + + + Column + + ${class.getJavaTypeName(version, jdk5)?html?default(" ")} + + <#if class.hasFieldJavaDoc(version)> + ${class.getFieldDescription(version)?default(" ")} + <#else> +   + +
    + <#else> + <#list superClasses as superClass> + <#if superClass.hasVersionProperty()> + <#assign version = superClass.versionProperty> + <#assign superClassRef = docFileManager.getRef(docFile, docFileManager.getEntityDocFileByDeclarationName(superClass))> +

    + Version Summary +

    + + + + + + + +
    + Version inherited from entity ${superClass.shortName} +
    + ${version.name} +
    + + + + + <#assign properties = dochelper.getOrderedSimpleProperties(class)> + <#if !properties.empty> + + + + + + + + + + + + + + + + <#list properties as property> + <#assign columnIterator = property.getValue().selectables.iterator()> + <#assign rowspan = property.getValue().getColumnSpan()> + + + + <#if (rowspan > 0)> + <#assign column = columnIterator.next()> + <#if column.isFormula()> + + <#else> + + + <#else> + + + + + + + + + + <#if (rowspan > 1)> + <#list columnIterator as column> + + + + + + + + +
    + Property Summary +
    + Name + + Column + + Access + + Type + + Description +
    0)>rowspan="${rowspan}"> + + ${property.name} + + +   + + + ${column.getName()} + + +   + 0)>rowspan="${rowspan}"> + ${property.getPropertyAccessorName()} (get / set) + 0)>rowspan="${rowspan}"> + <#if dochelper.getComponentPOJO(property)?exists> + + ${class.getJavaTypeName(property, jdk5)?html?default(" ")} + + <#else> + ${class.getJavaTypeName(property, jdk5)?html?default(" ")} + + 0)>rowspan="${rowspan}"> + <#if class.hasFieldJavaDoc(property)> + ${class.getFieldDescription(property)?default(" ")} + <#else> +   + +
    + + ${column.name} + +
    + + + <#assign propertyHeader = properties.empty> + <#list superClasses as superClass> + <#assign superProperties = dochelper.getOrderedSimpleProperties(superClass)> + <#if !superProperties.empty> + <#assign superClassRef = docFileManager.getRef(docFile, docFileManager.getEntityDocFileByDeclarationName(superClass))> + <#if propertyHeader> + <#assign propertyHeader = false> +

    + Property Summary +

    + + + + + + + + +
    + Properties inherited from entity ${superClass.shortName} +
    + <#list superProperties as property> + ${property.name}<#if property_has_next>, + +
    + + + + <#if class.hasIdentifierProperty()> + <#assign identifier = class.identifierProperty> +

    + Identifier Detail +

    + <#if dochelper.getComponentPOJO(identifier)?exists> + <#assign identifierClass = dochelper.getComponentPOJO(identifier)> + <#list identifierClass.allPropertiesIterator as property> +

    ${property.name}

    + + <#else> +

    ${identifier.name}

    + + + + <#if class.hasVersionProperty()> + <#assign version = class.versionProperty> +

    + Version Detail +

    + <#assign version = class.versionProperty> +

    ${version.name}

    + + + <#assign properties = dochelper.getSimpleProperties(class)> + <#if !properties.empty> +

    + Property Detail +

    + <#list properties as property> +

    ${property.name}

    +
    + + + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/entities/index.ftl b/tooling/hibernate-reveng/src/main/resources/doc/entities/index.ftl new file mode 100644 index 000000000000..ba304312b232 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/entities/index.ftl @@ -0,0 +1,38 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + + + Hibernate Mappings - Entity Mapping Information + + + + + + + + + + + <body> + <h2>Frame Alert</h2> + <p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.</p> + </body> + + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/entities/package-list.ftl b/tooling/hibernate-reveng/src/main/resources/doc/entities/package-list.ftl new file mode 100644 index 000000000000..84c2389888f4 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/entities/package-list.ftl @@ -0,0 +1,39 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + + + Hibernate Mappings - Package List + + + + +

    + All Entities +

    + +

    + Packages +

    +

    + <#list packageList as package> + ${package}
    + +

    + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/entities/package-summary.ftl b/tooling/hibernate-reveng/src/main/resources/doc/entities/package-summary.ftl new file mode 100644 index 000000000000..1c08d99ae930 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/entities/package-summary.ftl @@ -0,0 +1,58 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#import "/doc/common.ftl" as common> + + + + + + Hibernate Mappings - Entity Summary + + + + + <@common.header/> + +

    Package ${package}

    + + <#if (classList.size() > 0)> + + + + + + + + <#list classList as class> + + + + + + +
    + Entities Summary +
    + + ${class.declarationName} + + + ${class.getMetaAsString("class-description")?default(" ")} +
    + + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/entities/perPackageEntity-list.ftl b/tooling/hibernate-reveng/src/main/resources/doc/entities/perPackageEntity-list.ftl new file mode 100644 index 000000000000..0b078252d5d1 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/entities/perPackageEntity-list.ftl @@ -0,0 +1,39 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + + + Hibernate Mappings - Entity List + + + + +

    + ${title} +

    + + <#if (classList.size() > 0)> +

    + Entities
    + <#list classList as class> + ${class.declarationName}
    + +

    + + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/entities/summary.ftl b/tooling/hibernate-reveng/src/main/resources/doc/entities/summary.ftl new file mode 100644 index 000000000000..5d449ebf1cc6 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/entities/summary.ftl @@ -0,0 +1,60 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#import "/doc/common.ftl" as common> + + + + + + Hibernate Mappings - Entity Summary + + + + + <@common.header selected="entities"/> + +

    Hibernate Mapping Documentation

    + + <#if graphsGenerated> +

    + Entity Graph + + ${entitygrapharea} + +

    + + + + + + + + + + <#list packageList as package> + + + + + +
    + Packages +
    + ${package} +
    + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/hibernate_logo.gif b/tooling/hibernate-reveng/src/main/resources/doc/hibernate_logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..c17e7c2bdb70297dcf624ee79585256bea70c9d7 GIT binary patch literal 2515 zcmb8s`9IT-1Hke34s&gpdqcG?Ur}gLqWFAw&}Xhb$Zho*xpIBxDiLkGw?<^n+%^s4 zgO+QQG`9+0eJV$ZDLL}(6McN7Iq zKejhMZTiUP(we=4x^5x}~|E|o)mseKbO)bmE=jWGS&(FVo zJv=@4dj93;)a=~!^z{5x&*bwLubxYvPqa>rH;;{t4NN?J_PB2F(fIR6b%P@#lfyL= zgB87l!}nzaV-NC29u)TXh=%Xq?C9xzBE2#w&1#k2Ym>GOb#R6{QkvU4W$o#*))c9v z;8#hT_)goymV};`l-i~{(x#NI#(1fi*&$B8UEk1A8`&&MDXDF&5YRuf@luX2e~+ zb}^0JQsU8Fgo5ZGBzMQ+Alc5BPjB0V5l4Yf(t#u zB_P<*=bWP#o#Np^b@w~z(D-XpbITpc-PX4Dj!tP;_r3c)4|*TU`uYb3 zhlWQQjvRb4HvR;~H}~>#RN>*ydjudsgoBUINhpY&^;lVTKcR7*;Jdov;cEr%b>H;x za=>1P>_7Cx8dt`G4)LIwII*u7PZFZcDjextv<&2F29lVD>}(AKTS}Ieaxh#(h@4`^ zvciYTEn{X-PHr5OzR_C*JL$?YvaC9Mf{)KIT)|;ulE1>UhFvu%Mvsj{{7)kb;2dZ> zpkXlrQuUL!Sh0XRph2hZF$OP*Ugat6!eeottPIsrH>*n4S~E`He8_@)-gzHR`^nT) zt(kc?)D?{JNHaRB0>Ixkp0~JRGH_))mtsD})PdC{T(qPr70uASvLh!ds;v#QND8?R zZ9SXJMEKwvSzPp3*I&0b1Zp;+#?|Uc-JeW>lJ#d)~Vf!55R56mR9JMz1d&$SC|$!kUv+{h4*n^5imQLw2tnU2TMHma z`ZJZ9JI&L<7D;WO0_x)S>;Nnq!yVQ};5?whp?mcOHBX@eSzU}R8C&>uZ`{R>^b*Nl z?ABzc{w@l`mxroZ_F!7@Fl7A!VD1wMR)V|V&{APLiOp1a_U2J3)@frlEUA|8+W}o8 zI=}BfqyQoqPH7yelBHv2*#O*44%G>jKu!zOH%<(z=m3>{3UINN8AcaLNY}l$9l05; zsRdv%<>xVU{~dFpftt~S#?~gaSO*wf>6l!)kf=idG$Tfl0T{BJm2oEn^4J8+2sY|o zYSNBgeL51?>7ebCwca_^+Om7@6nq}V7U}NiB_M1>Xl4m^x<+g zI-3EM@XPG!zaZTi>4+2JzQfGMlm)7P2qqq36274vG&8Z1ML9X)f>+kR3e^yM<7^w) z%DU((9KrD407-DzP7DD11P{sIC!LK|XF(c6>Z|?nh#{{=%MpFczKL;~dzPw=Sh^zw zUBj*~4&#d*OmE{O7$M97U4^^Q)>?JAsJyVvv;%uISZL{Q;r!te28w#e0PGDk1B^VS zWC2bwT}z)T52*?b zvvVy|WQ4-^>*+cuAsCAXT~_^H7&;ZQdK*a7t2bo`;GXP=za!rvi|K@WK z@5KoT=Y6+kbQFbcR$4$Ki%Fhevm5pooak{tw0jr00a%HctKu(7kaUxeryg^+3Ti8O z`8`-qRma5WwZPLYKmTEGaRXqSi45OOqL?mRwBK0OfUvL-#ITb$lDY1Bs?mh>$8xRA z?#-F%!wm#eA|feuLRGV9Rbu-7k$P_|8adh5o=osE4#5F%T2zq9A4F<&pkp{6^1;oY zr%n^s>X8hQV^Zq!7NC&mAuBeVq>3frb$ z!830z_13U8k$R3_cD_uO3tB+tQigd6nIpMk6sGRF=IBDIvd-!gyz-fFO1Y;2A+h&= zd(JsV(nD2!ZErjnYyb0*OzG4+q+X9~$m`Whb-ZlrvM)&?qjP&o778+T{;I_B0t?@x zI$L##LdZGA)6b~vx2^T5N{|YZ37Z{gQNwsNg~Wo%_ei^J_|V40Z*!|>r1z%Hcq9Ng F^*^G8QlkI> literal 0 HcmV?d00001 diff --git a/tooling/hibernate-reveng/src/main/resources/doc/index.html b/tooling/hibernate-reveng/src/main/resources/doc/index.html new file mode 100644 index 000000000000..770748b98d6a --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/index.html @@ -0,0 +1,25 @@ + + + + + + Hibernate Mappings - Hibernate Mapping Information + + + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/inherit.gif b/tooling/hibernate-reveng/src/main/resources/doc/inherit.gif new file mode 100644 index 0000000000000000000000000000000000000000..6833399cc473c5872f9a18c06b5ca94fa2d2903b GIT binary patch literal 71 zcmZ?wbhEHbt=OVO;Xk5?vG Q-)|6fn6|9dkCnk10K=#ijQ{`u literal 0 HcmV?d00001 diff --git a/tooling/hibernate-reveng/src/main/resources/doc/tables/index.ftl b/tooling/hibernate-reveng/src/main/resources/doc/tables/index.ftl new file mode 100644 index 000000000000..b38a59f9791a --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/tables/index.ftl @@ -0,0 +1,38 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + + + Hibernate Mappings - Table Mapping Information + + + + + + + + + + + <body> + <h2>Frame Alert</h2> + <p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.</p> + </body> + + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/tables/schema-list.ftl b/tooling/hibernate-reveng/src/main/resources/doc/tables/schema-list.ftl new file mode 100644 index 000000000000..58ae7f80f5db --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/tables/schema-list.ftl @@ -0,0 +1,40 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + + + Hibernate Mappings - Schema List + + + + +

    + All Tables +

    + +

    + Schemas +

    + +

    + <#list schemaList as schema> + ${schema}
    + +

    + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/tables/schema-summary.ftl b/tooling/hibernate-reveng/src/main/resources/doc/tables/schema-summary.ftl new file mode 100644 index 000000000000..8d30896f8d68 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/tables/schema-summary.ftl @@ -0,0 +1,53 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#import "/doc/common.ftl" as common> + + + + + + Hibernate Mappings - Table Summary + + + + + <@common.header/> + +

    Schema ${schema}

    + + + + + + + + + <#list dochelper.tablesBySchema.get(schema) as table> + + + + + +
    + Tables +
    + + ${table.name} + +
    + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/tables/schema-table-list.ftl b/tooling/hibernate-reveng/src/main/resources/doc/tables/schema-table-list.ftl new file mode 100644 index 000000000000..e166698044cc --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/tables/schema-table-list.ftl @@ -0,0 +1,36 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + + + Hibernate Mappings - Table List + + + + +

    + ${title} +

    + +

    + <#list tableList as table> + ${table.name}
    + +

    + + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/doc/tables/summary.ftl b/tooling/hibernate-reveng/src/main/resources/doc/tables/summary.ftl new file mode 100644 index 000000000000..a2f1baadcf48 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/tables/summary.ftl @@ -0,0 +1,62 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#import "/doc/common.ftl" as common> + + + + + + Hibernate Mappings - Table Summary + + + + + <@common.header selected="tables"/> + +

    Hibernate Mapping Documentation

    + + <#if graphsGenerated> +

    + Table Graph + + ${tablegrapharea} + +

    + + + + + + + + + + <#list dochelper.tablesBySchema.keySet() as schema> + + + + + +
    + Schemas +
    + + ${schema} + +
    + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/tables/table-list.ftl b/tooling/hibernate-reveng/src/main/resources/doc/tables/table-list.ftl new file mode 100644 index 000000000000..162375d13d29 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/tables/table-list.ftl @@ -0,0 +1,36 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + + + Hibernate Mappings - Table List + + + + +

    + All Tables +

    + +

    + <#list tableList as table> + ${table.name}
    + +

    + + + diff --git a/tooling/hibernate-reveng/src/main/resources/doc/tables/table.ftl b/tooling/hibernate-reveng/src/main/resources/doc/tables/table.ftl new file mode 100644 index 000000000000..e268af3c9191 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/doc/tables/table.ftl @@ -0,0 +1,308 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#import "/doc/common.ftl" as common> + + + + + + Hibernate Mappings - Table Info + + + + + <@common.header/> + +

    Schema ${dochelper.getQualifiedSchemaName(table)}

    +

    Table ${table.name}

    + + <#if table.comment?exists> +

    ${table.comment}

    + + + + + + + + + + + + + + + + + + + <#list table.columns as column> + + + + + + + + + + + +
    + Column Summary +
    + Name + + SqlType + + Length + + Precision + + Scale + + Nullable + + Unique +
    + + ${column.name} + + + ${dochelper.getSQLTypeName(column)} + + ${dochelper.getLength(column)} + + ${dochelper.getPrecision(column)} + + ${dochelper.getScale(column)} + + ${column.nullable?string} + + ${column.unique?string} +
    + + <#-- SHOW THE PRIMARY KEY --> + + + + + + + + + + + + <#if table.hasPrimaryKey()> + + + + + <#else> + + + + + +
    + Primary Key +
    + Name + + Columns +
    + <#if table.primaryKey.name?has_content> + ${table.primaryKey.name} + <#else> + Name not specified + + + <#list dochelper.getPrimaryKeyColumnIterator(table) as column> + + ${column.name} + + +
    + No Primary Key +
    + + <#-- SHOW THE FOREIGN KEYS --> + <#if !table.foreignKeys.isEmpty()> + + + + + + + + + + + + + <#list table.foreignKeys.values() as foreignKey> + + + + + + + +
    + Foreign Keys +
    + Name + + Referenced Table + + Columns +
    + ${foreignKey.name?default("Name not specified")} + + + ${foreignKey.referencedTable.name} + + + <#list foreignKey.columns as column> + + ${column.name} + + +
    + + + <#-- SHOW THE UNIQUE KEYS --> + <#if !table.uniqueKeys.isEmpty()> + + + + + + + + + + + + <#list table.uniqueKeys.values() as uniqueKey> + + + + + + +
    + Unique Keys +
    + Name + + Columns +
    + ${uniqueKey.name?default("Name not specified")} + + <#list uniqueKey.columns as column> + + ${column.name} + + +
    + + + <#-- SHOW THE TABLE INDEXES --> + <#if !table.indexes.isEmpty()> + + + + + + + + + + + + <#list table.indexes.values() as index> + + + + + + +
    + Indexes +
    + Name + + Columns +
    + ${index.name?default("Name not specificed")} + + <#list index.columns as column> + + ${column.name} + + +
    + + + <#if !table.columns.isEmpty()> +

    + Column Detail +

    + + <#list table.columns as column> +

    ${column.name}

    + +
      +
    • + Type: ${dochelper.getSQLTypeName(column)} +
    • +
    • + Length: ${dochelper.getLength(column)} +
    • +
    • + Precision: ${dochelper.getPrecision(column)} +
    • +
    • + Scale: ${dochelper.getScale(column)} +
    • +
    • + Nullable: ${column.nullable?string} +
    • +
    • + Unique: ${column.unique?string} +
    • +
    • + Comment: ${column.comment?if_exists} +
    • +
    + + <#-- +

    Mappings:

    + + <#list dochelper.getProperties(table, column) as property> + property.persistentClass.className - property.name + + --> + +
    + + + + + + diff --git a/tooling/hibernate-reveng/src/main/resources/dot/entitygraph.dot.ftl b/tooling/hibernate-reveng/src/main/resources/dot/entitygraph.dot.ftl new file mode 100644 index 000000000000..9f7e5b99386d --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/dot/entitygraph.dot.ftl @@ -0,0 +1,90 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +digraph EntityGraph { + compound=true; + bgcolor="white"; + fontcolor="black"; fontname="Helvetica"; fontsize="10.0"; + ranksep="equally"; + label="Entity Graph"; + URL="http://tools.hibernate.org"; + edge [ + color="lightgrey", + fontcolor="black", + fontname="Helvetica", + fontsize="8.0", + labelangle=-25.0, + labeldistance=1.5 + ]; + node [ + fontcolor="black", + fontname="Helvetica", + fontsize="10.0", + shape=record, + fillcolor="#D4E5FE", + style="solid,filled"]; + +<#list md.entityBindings as entity> + /* Node ${entity.entityName} */ + <@nodeName entity.entityName/> [ label = "<@propertyLabels name=entity.entityName properties=entity.properties/>", URL="${entity.entityName?replace(".","/")}.html" ] + /* Subclass edges for ${entity.entityName} */ + <#list entity.getDirectSubclasses() as subclass> + <@nodeName subclass.entityName/> -> <@nodeName entity.entityName/> [ weight="10", arrowhead="onormal" ] + + + <@propertyEdges root=entity.entityName?replace(".","_dot_") properties=entity.properties/> + + +} + +<#macro nodeName name>${name?replace(".","_dot_")} + +<#macro propertyLabels name properties> +<@compress single_line=true> + { + ${name?replace(".","\\.")}| + <#list properties as p> + <#if p.value.isSimpleValue()> + ${p.name}\l + + + } + +<#macro dumpComponent compProperty> + <#assign component=compProperty.value> + /* Node component ${component} */ + ${c2h.getHibernateTypeName(compProperty)?replace(".","_dot_")} [ + label = "<@propertyLabels name=component.componentClassName properties=component.properties/>" + ] + <@propertyEdges root=component.componentClassName?replace(".","_dot_") properties=component.properties/> + + +<#macro propertyEdges root properties> + /* Property edges/nodes for ${root} */ + <#list properties as property> + <#if c2h.getSafeHibernateTypeName(property)?exists> + ${root} -> ${c2h.getHibernateTypeName(property)?replace(".","_dot_")} [ + label="${property.name}" + <#if c2j.isComponent(property)> + arrowtail="diamond" + + ] + + <#if c2j.isComponent(property)> + <@dumpComponent property/> + + + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/dot/tablegraph.dot.ftl b/tooling/hibernate-reveng/src/main/resources/dot/tablegraph.dot.ftl new file mode 100644 index 000000000000..45faf7d39152 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/dot/tablegraph.dot.ftl @@ -0,0 +1,72 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +digraph TableGraph { + compound=true; + bgcolor="white"; + fontcolor="black"; fontname="Helvetica"; fontsize="10.0"; + ranksep="equally"; + label="Table Graph"; + URL="http://tools.hibernate.org"; + edge [ + color="lightgrey", + fontcolor="black", + fontname="Helvetica", + fontsize="8.0", + labelangle=-25.0, + labeldistance=1.5 + headport=nw, + tailport=se + ]; + node [ + fontcolor="black", + fontname="Helvetica", + fontsize="10.0", + shape=record, + fillcolor="yellow", + style="solid,filled"]; + +/* TODO: multi schema tables */ +<#list tables as table> + <#if table.isPhysicalTable()> + /* Node ${table.name} */ + <@nodeName table/> [ label = "<@columnLabels name=table.name columns=table.columns/>" ] + + <@propertyEdges root=table.name?replace(".","_dot_") foreignKeys=table.foreignKeys.values()/> + + + +} + +<#macro nodeName table>${table.name?replace(".","_dot_")} + +<#macro columnLabels name columns> +<@compress single_line=true> + { + ${name?replace(".","\\.")}| + <#list columns as p> + <${p.name}>${p.name}\l + <#if p_has_next>| + + } + +<#macro propertyEdges root foreignKeys> + /* edges/nodes for ${root} */ + <#list foreignKeys as fk> + ${root} -> <@nodeName fk.referencedTable/> [ + label="${fk.name}" + ] + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/any.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/any.hbm.ftl new file mode 100644 index 000000000000..e67e780190fc --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/any.hbm.ftl @@ -0,0 +1,37 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#assign value = property.value> + + cascade="${property.cascade}" + + <#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + > + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + <#if value.metaValues?exists> + <#list value.metaValues.entrySet() as entry> + + + + <#list property.columns as column> + <#include "column.hbm.ftl"> + + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/array.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/array.hbm.ftl new file mode 100644 index 000000000000..4354b8cb73ab --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/array.hbm.ftl @@ -0,0 +1,41 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#assign value = property.value> +<#assign keyValue = value.getKey()> +<#assign elementValue = value.getElement()> +<#assign indexValue = value.getIndex()> +<#assign elementTag = c2h.getCollectionElementTag(property)> + + element-class="${value.elementClassName}" + <#include "collection-tableattr.hbm.ftl"> + <#if property.cascade != "none"> + cascade="${property.cascade}" + + <#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + + <#if c2h.hasFetchMode(property)> fetch="${c2h.getFetchMode(property)}"> + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + <#include "key.hbm.ftl"> + + <#list indexValue.columns as column> + <#include "column.hbm.ftl"> + + + <#include "${elementTag}-element.hbm.ftl"> + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/bag.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/bag.hbm.ftl new file mode 100644 index 000000000000..ddf4699d6d5f --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/bag.hbm.ftl @@ -0,0 +1,36 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#assign value = property.value> +<#assign keyValue = value.getKey()> +<#assign elementValue = value.getElement()> +<#assign elementTag = c2h.getCollectionElementTag(property)> + + + inverse="${value.inverse?string}" + lazy="${c2h.getCollectionLazy(value)}" + <#if property.cascade != "none"> + cascade="${property.cascade}" + + <#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + + <#if c2h.hasFetchMode(property)> fetch="${c2h.getFetchMode(property)}"> + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + <#include "key.hbm.ftl"> + <#include "${elementTag}-element.hbm.ftl"> + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/collection-tableattr.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/collection-tableattr.hbm.ftl new file mode 100644 index 000000000000..43f77c340639 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/collection-tableattr.hbm.ftl @@ -0,0 +1,22 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +table="${value.collectionTable.quotedName}" +<#if value.collectionTable.catalog?exists && ((clazz.table.catalog?exists && clazz.table.catalog!=value.collectionTable.catalog) || (!clazz.table.catalog?exists && value.collectionTable.catalog?exists)) > +catalog="${value.collectionTable.catalog}" + +<#if value.collectionTable.schema?exists && ((clazz.table.schema?exists && clazz.table.schema!=value.collectionTable.schema) || (!clazz.table.schema?exists && value.collectionTable.schema?exists)) > +schema="${value.collectionTable.quotedSchema}" + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/column.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/column.hbm.ftl new file mode 100644 index 000000000000..55e69bdec697 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/column.hbm.ftl @@ -0,0 +1,23 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#if column.isFormula()> +${column.getFormula()} +<#else> +> +${column.comment} +<#else>/> + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/component.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/component.hbm.ftl new file mode 100644 index 000000000000..32bdebca97b3 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/component.hbm.ftl @@ -0,0 +1,28 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + access="${property.propertyAccessorName}" + > + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + <#list c2h.getProperties(property.value) as property> + <#include "${c2h.getTag(property)}.hbm.ftl"/> + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/composite-element.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/composite-element.hbm.ftl new file mode 100644 index 000000000000..c7c50bd6e3cf --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/composite-element.hbm.ftl @@ -0,0 +1,27 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + <#list elementValue.properties as property> + <#assign tag=c2h.getTag(property)> + <#if tag="component"> + <#assign tag="nested-composite-element"> + <#assign elementValue = property.value> + + <#include "${tag}.hbm.ftl"/> + + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/dynamic-component.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/dynamic-component.hbm.ftl new file mode 100644 index 000000000000..4cb099dad5ce --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/dynamic-component.hbm.ftl @@ -0,0 +1,28 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + access="${property.propertyAccessorName}" + + > + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + <#list c2h.getProperties(property.value) as property> + <#include "${c2h.getTag(property)}.hbm.ftl"/> + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/element-element.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/element-element.hbm.ftl new file mode 100644 index 000000000000..5fed01b2ca5b --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/element-element.hbm.ftl @@ -0,0 +1,20 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + <#list elementValue.columns as column> + <#include "column.hbm.ftl"> + + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/filter-def.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/filter-def.hbm.ftl new file mode 100644 index 000000000000..2572c05f4ffb --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/filter-def.hbm.ftl @@ -0,0 +1,23 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#list md.filterDefinitions.keySet() as filterKey> +<#assign filterDef = md.filterDefinitions.get(filterKey)> + + <#list filterDef.parameterNames as filterParaName> + + + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/generalhbm.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/generalhbm.hbm.ftl new file mode 100644 index 000000000000..72e2e18e73de --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/generalhbm.hbm.ftl @@ -0,0 +1,41 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + + + + +<#if c2h.isImportData(md)> +<#include "import.hbm.ftl"> + +<#if c2h.isNamedQueries(md)> +<#include "query.hbm.ftl"> + +<#if c2h.isNamedSQLQueries(md)> +<#include "sql-query.hbm.ftl"> + +<#if c2h.isFilterDefinitions(md)> +<#include "filter-def.hbm.ftl"> + + + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/hibernate-mapping.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/hibernate-mapping.hbm.ftl new file mode 100644 index 000000000000..d616d0b1bb30 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/hibernate-mapping.hbm.ftl @@ -0,0 +1,51 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + + +<#if hmgs?exists && hmgs.hasNonDefaultSettings()> + + package="${hmgs.defaultPackage}" + +<#if hmgs.hasSchemaName()> + schema="${hmgs.schemaName}" + +<#if hmgs.hasCatalogName()> + catalog="${hmgs.catalogName}" + +<#if hmgs.hasNonDefaultCascade()> + default-cascade="${hmgs.defaultCascade}" + +<#if hmgs.hasNonDefaultAccess()> + default-access="${hmgs.defaultAccess}" + +<#if !hmgs.isDefaultLazy()> + default-lazy="false" + +<#if !hmgs.isAutoImport()> + auto-import="false" +> +<#else> + + + +<#include "persistentclass.hbm.ftl"/> + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/id.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/id.hbm.ftl new file mode 100644 index 000000000000..c82dbfe6f1e5 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/id.hbm.ftl @@ -0,0 +1,89 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#if embeddedid?exists> + + <#list embeddedid.properties as keyproperty> + <#if !c2h.isManyToOne(keyproperty)> + + <#list keyproperty.columns as column> + <#include "pkcolumn.hbm.ftl"> + + + <#else> + + <#list keyproperty.columns as column> + <#include "pkcolumn.hbm.ftl"> + + + + + +<#elseif !c2j.isComponent(property)> + + unsaved-value="${c2h.getUnsavedValue(property)}" + + <#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + + > + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + + <#list property.columns as column> + <#include "pkcolumn.hbm.ftl"> + + <#if !c2h.isIdentifierGeneratorProperties(property)> + + <#else> + + <#assign parameters = c2h.getIdentifierGeneratorProperties(property)> + <#list c2h.getFilteredIdentifierGeneratorKeySet(property, props) as paramkey> + ${parameters.get(paramkey)} + + + + +<#else> + + unsaved-value="${c2h.getUnsavedValue(property)}" + +<#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + + > + <#list property.value.properties as keyproperty> + <#if !c2h.isManyToOne(keyproperty)> + + <#list keyproperty.columns as column> + <#include "pkcolumn.hbm.ftl"> + + + <#else> + + <#list keyproperty.columns as column> + <#include "pkcolumn.hbm.ftl"> + + + + + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/idbag.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/idbag.hbm.ftl new file mode 100644 index 000000000000..6e75966fc045 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/idbag.hbm.ftl @@ -0,0 +1,44 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#assign value = property.value> +<#assign keyValue = value.getKey()> +<#assign elementValue = value.getElement()> +<#assign elementTag = c2h.getCollectionElementTag(property)> + + + lazy="${c2h.getCollectionLazy(value)}" + <#if property.cascade != "none"> + cascade="${property.cascade}" + + <#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + + <#if c2h.hasFetchMode(property)> fetch="${c2h.getFetchMode(property)}"> + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + + + + + <#list keyValue.columns as column> + <#include "column.hbm.ftl"> + + + <#include "${elementTag}-element.hbm.ftl"> + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/import.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/import.hbm.ftl new file mode 100644 index 000000000000..764861799786 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/import.hbm.ftl @@ -0,0 +1,20 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#list md.imports.keySet() as importKey> +<#assign importDef = md.imports.get(importKey)> +<#if !importKey.equals(importDef)> + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/key.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/key.hbm.ftl new file mode 100644 index 000000000000..74a037a4fad8 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/key.hbm.ftl @@ -0,0 +1,23 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + property-ref="${property.value.referencedPropertyName}" +> + <#list keyValue.columns as column> + <#include "column.hbm.ftl"> + + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/list.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/list.hbm.ftl new file mode 100644 index 000000000000..470d9e26b12c --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/list.hbm.ftl @@ -0,0 +1,43 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#assign value = property.value> +<#assign keyValue = value.getKey()> +<#assign elementValue = value.getElement()> +<#assign indexValue = value.getIndex()> +<#assign elementTag = c2h.getCollectionElementTag(property)> + + + lazy="${c2h.getCollectionLazy(value)}" + <#if property.cascade != "none"> + cascade="${property.cascade}" + + <#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + + <#if c2h.hasFetchMode(property)> fetch="${c2h.getFetchMode(property)}"> + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + <#include "key.hbm.ftl"> + + <#list indexValue.columns as column> + <#include "column.hbm.ftl"> + + + <#include "${elementTag}-element.hbm.ftl"> + + + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/many-to-any-element.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/many-to-any-element.hbm.ftl new file mode 100644 index 000000000000..b65e678a352b --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/many-to-any-element.hbm.ftl @@ -0,0 +1,26 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + <#if elementValue.metaValues?exists> + <#list elementValue.metaValues.entrySet() as entry> + + + + <#list elementValue.columns as column> + <#include "column.hbm.ftl"> + + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/many-to-many-element.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/many-to-many-element.hbm.ftl new file mode 100644 index 000000000000..5ff01f15a5bf --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/many-to-many-element.hbm.ftl @@ -0,0 +1,23 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + +<#if property.value.referencedPropertyName?exists> + property-ref="${property.value.referencedPropertyName}" +> + <#list elementValue.selectables as column> + <#include "column.hbm.ftl"> + + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/many-to-one.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/many-to-one.hbm.ftl new file mode 100644 index 000000000000..f8bda83a82e2 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/many-to-one.hbm.ftl @@ -0,0 +1,48 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + property-ref="${property.value.referencedPropertyName}" + +<#if !property.updateable> + update="false" + +<#if !property.insertable> + insert="false" + +<#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + +<#if property.cascade != "none"> + cascade="${property.cascade}" + +<#assign fetchmode = c2h.getFetchMode(property)> +<#if fetchmode != "default"> + fetch="${fetchmode}" + +<#if !property.optimisticLocked> + optimistic-lock="false" + + > + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> +<#list property.columns as column> + <#include "column.hbm.ftl"> + + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/map.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/map.hbm.ftl new file mode 100644 index 000000000000..798c2627cfba --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/map.hbm.ftl @@ -0,0 +1,49 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#assign value = property.value> +<#assign keyValue = value.getKey()> +<#assign elementValue = value.getElement()> +<#assign indexValue = value.getIndex()> +<#assign elementTag = c2h.getCollectionElementTag(property)> + + + lazy="${c2h.getCollectionLazy(value)}" + <#if property.cascade != "none"> + cascade="${property.cascade}" + + <#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + + <#if c2h.hasFetchMode(property)> fetch="${c2h.getFetchMode(property)}"> + <#assign metaattributable=property><#include "meta.hbm.ftl"> + <#-- TODO table attributes--> + <#include "key.hbm.ftl"> + <#if c2h.isManyToOne(indexValue)> + + <#list indexValue.selectables as column> + <#include "column.hbm.ftl"> + + + <#else> + + <#list indexValue.selectables as column> + <#include "column.hbm.ftl"> + + + + <#include "${elementTag}-element.hbm.ftl"> + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/meta.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/meta.hbm.ftl new file mode 100644 index 000000000000..f5d13bcb3c7f --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/meta.hbm.ftl @@ -0,0 +1,22 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#if metaattributable.getMetaAttributes()?exists> +<#list metaattributable.getMetaAttributes().keySet() as key> + <#list metaattributable.getMetaAttributes().get(key).values as value> + ${value} + + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/nested-composite-element.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/nested-composite-element.hbm.ftl new file mode 100644 index 000000000000..1e46ec4ebec7 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/nested-composite-element.hbm.ftl @@ -0,0 +1,27 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + <#list elementValue.properties as property> + <#assign tag=c2h.getTag(property)> + <#if tag="component"> + <#assign tag="nested-composite-element"> + <#assign elementValue = property.value> + + <#include "${tag}.hbm.ftl"/> + + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/one-to-many-element.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/one-to-many-element.hbm.ftl new file mode 100644 index 000000000000..eff8b2c1df1e --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/one-to-many-element.hbm.ftl @@ -0,0 +1,19 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + entity-name="${elementValue.getReferencedEntityName()}" + /> diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/one-to-one.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/one-to-one.hbm.ftl new file mode 100644 index 000000000000..b733bdaaa0bf --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/one-to-one.hbm.ftl @@ -0,0 +1,39 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + property-ref="${property.value.referencedPropertyName}" + +<#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + +<#if property.cascade != "none"> + cascade="${property.cascade}" + +<#if property.value.constrained> + constrained="true" + + +<#if property.value.hasFormula()> +<#assign formula = c2h.getFormulaForProperty(property)> +<#if formula?exists> + formula="${formula.text}" + + + > + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/persistentclass.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/persistentclass.hbm.ftl new file mode 100644 index 000000000000..0da1552a28d5 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/persistentclass.hbm.ftl @@ -0,0 +1,161 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<${c2h.getTag(clazz)} + name="${c2h.getClassName(clazz)}" +<#if !c2h.getClassName(clazz).equals(clazz.entityName)> + entity-name="${clazz.entityName}" + +<#if clazz.superclass?exists> + extends="${clazz.getSuperclass().className}" + +<#if c2h.needsTable(clazz)> + table="${clazz.table.quotedName}" + +<#if !c2h.isSubclass(clazz) && clazz.table.schema?exists> + schema="${clazz.table.quotedSchema}" + +<#if !c2h.isSubclass(clazz) && clazz.table.catalog?exists> + catalog="${clazz.table.catalog}" + +<#if !clazz.mutable> + mutable="false" + +<#if clazz.useDynamicUpdate()> + dynamic-update="true" + +<#if clazz.useDynamicInsert()> + dynamic-insert="true" + +<#if clazz.hasSelectBeforeUpdate()> + select-before-update="true" + +<#if c2h.needsDiscriminator(clazz)> + discriminator-value="${clazz.discriminatorValue}" + +<#if clazz.isExplicitPolymorphism()> + polymorphism="explicit" + +<#if clazz.isLazy() && !c2h.getClassName(clazz).equals(c2h.getProxyInterfaceName(clazz))> + proxy="${c2h.getProxyInterfaceName(clazz)}" +<#elseif !clazz.isLazy()> + lazy="false" + +<#if clazz.isAbstract()?exists && clazz.isAbstract()> + abstract="true" + +<#if c2h.isClassLevelOptimisticLockMode(clazz)> + optimistic-lock="${c2h.getClassLevelOptimisticLockMode(clazz)}" + +<#if (clazz.batchSize>1)> + batch-size="${clazz.batchSize}" + +<#if clazz.where?exists> + where="${clazz.where}" + +<#if clazz.table.subselect> + subselect="${clazz.table.getSubselect()}" + +<#if c2h.hasCustomEntityPersister(clazz)> + persister="${clazz.getEntityPersisterClass().name}" + +<#if clazz.table.rowId?exists> + rowid="${clazz.table.rowId}" +> +<#assign metaattributable=clazz/> +<#include "meta.hbm.ftl"/> + +<#if clazz.table.comment?exists && clazz.table.comment?trim?length!=0> + ${clazz.table.comment} + +<#-- TODO: move this to id.hbm.ftl --> +<#if !c2h.isSubclass(clazz)> + <#if clazz.hasIdentifierProperty()> + <#assign property=clazz.getIdentifierProperty()/> + <#include "id.hbm.ftl"/> + <#elseif clazz.hasEmbeddedIdentifier()> + <#assign embeddedid=clazz.key/> + <#include "id.hbm.ftl"/> + <#elseif clazz.identifier?exists> + <#if c2j.isComponent(clazz.identifier)> + mapped="true"> + <#list clazz.identifier.properties as property> + + <#list property.value.selectables as column> + <#include "column.hbm.ftl"> + + + + + <#else> + + <#list clazz.identifier.selectables as column> + <#include "column.hbm.ftl"> + + + + +<#elseif c2h.isJoinedSubclass(clazz)> + + <#list clazz.key.columns as column> + <#include "column.hbm.ftl"> + + + + +<#if c2h.needsDiscriminatorElement(clazz)> + + <#list clazz.discriminator.selectables as column> + <#include "column.hbm.ftl"> + + + + + +<#-- version has to be done explicitly since Annotation's does not list version first --> +<#if pojo.hasVersionProperty()> +<#assign property=clazz.getVersion()/> +<#include "${c2h.getTag(property)}.hbm.ftl"/> + + +<#list c2h.getProperties(clazz) as property> +<#if c2h.getTag(property)!="version" && c2h.getTag(property)!="timestamp"> +<#include "${c2h.getTag(property)}.hbm.ftl"/> + + + +<#list clazz.joins as join> + + table="${join.table.name}" + > + <#if join.table.comment?exists && join.table.comment?trim?length!=0> + ${comment} + + + <#list join.key.selectables as column> + <#include "column.hbm.ftl"> + + + <#list join.properties as property> + <#include "${c2h.getTag(property)}.hbm.ftl"/> + + + + + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/pkcolumn.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/pkcolumn.hbm.ftl new file mode 100644 index 000000000000..65c304257b48 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/pkcolumn.hbm.ftl @@ -0,0 +1,16 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/primitive-array.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/primitive-array.hbm.ftl new file mode 100644 index 000000000000..38b08e896f59 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/primitive-array.hbm.ftl @@ -0,0 +1,41 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#assign value = property.value> +<#assign table = value.collectionTable.name> +<#assign dependentValue = value.getKey()> + + + access="${property.propertyAccessorName}" +> + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + + <#list dependentValue.selectables as column> + <#include "column.hbm.ftl"> + + + + <#list value.index.selectables as column> + <#include "column.hbm.ftl"> + + + + <#list value.element.selectables as column> + <#include "column.hbm.ftl"> + + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/properties.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/properties.hbm.ftl new file mode 100644 index 000000000000..35377bd490b1 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/properties.hbm.ftl @@ -0,0 +1,24 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + <#list c2h.getProperties(property.value) as property> + <#include "${c2h.getTag(property)}.hbm.ftl"/> + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/property.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/property.hbm.ftl new file mode 100644 index 000000000000..190a68c7c6a2 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/property.hbm.ftl @@ -0,0 +1,58 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + + type="${property.value.typeName}" + +<#if !property.updateable> + update="false" + +<#if !property.insertable> + insert="false" + +<#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + +<#if property.lazy> + lazy="true" + +<#if !property.optimisticLocked> + optimistic-lock="false" + +<#if property.value.hasFormula()> +<#assign formula = c2h.getFormulaForProperty(property)> +<#if formula?has_content> + formula="${formula.text}" + + + > + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + <#list property.selectables as column> + <#if !column.isFormula()> + <#include "column.hbm.ftl"> + + + <#if property.value.typeParameters?exists> + + <#list property.value.typeParameters.entrySet() as entry> + ${entry.value} + + + + + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/query.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/query.hbm.ftl new file mode 100644 index 000000000000..86e97dea81bf --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/query.hbm.ftl @@ -0,0 +1,36 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#list md.namedQueryDefinitions as queryDef> + + flush-mode="${queryDef.flushMode.toString().toLowerCase()}" + +<#if queryDef.isCacheable()> + cacheable="${queryDef.isCacheable()?string}" + +<#if queryDef.cacheRegion?exists> + cache-region="${queryDef.cacheRegion}" + +<#if queryDef.fetchSize?exists> + fetch-size="${queryDef.fetchSize}" + +<#if queryDef.timeout?exists> + timeout="${queryDef.timeout?c}" + > + + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/set.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/set.hbm.ftl new file mode 100644 index 000000000000..e15106d60af4 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/set.hbm.ftl @@ -0,0 +1,37 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#assign value = property.value> +<#assign keyValue = value.getKey()> +<#assign elementValue = value.getElement()> +<#assign elementTag = c2h.getCollectionElementTag(property)> + + + inverse="${value.inverse?string}" + lazy="${c2h.getCollectionLazy(value)}" + <#if property.cascade != "none"> + cascade="${property.cascade}" + + <#if !property.basicPropertyAccessor> + access="${property.propertyAccessorName}" + + <#if c2h.hasFetchMode(property)> fetch="${c2h.getFetchMode(property)}" + > + <#assign metaattributable=property> + <#include "meta.hbm.ftl"> + <#include "key.hbm.ftl"> + <#include "${elementTag}-element.hbm.ftl"> + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/sql-query.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/sql-query.hbm.ftl new file mode 100644 index 000000000000..eb2e3b87fc33 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/sql-query.hbm.ftl @@ -0,0 +1,60 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#list md.namedNativeQueryDefinitions as queryDef> + + flush-mode="${queryDef.flushMode.toString().toLowerCase()}" + +<#if queryDef.isCacheable()> + cacheable="${queryDef.isCacheable()?string}" + +<#if queryDef.cacheRegion?exists> + cache-region="${queryDef.cacheRegion}" + +<#if queryDef.fetchSize?exists> + fetch-size="${queryDef.fetchSize}" + +<#if queryDef.timeout?exists> + timeout="${queryDef.timeout?c}" + +> +<#if queryDef.querySpaces?exists> +<#list queryDef.querySpaces as tableName> + + + +<#if queryDef.queryReturns?exists> +<#list queryDef.queryReturns as returnDef> +<#assign returnTag = c2h.getNamedSQLReturnTag(returnDef)> + <${returnTag} + alias="${returnDef.alias}" +<#if c2h.isNamedSQLReturnRoot(returnDef)> + class="${returnDef.returnEntityName}" +<#elseif c2h.isNamedSQLReturnRole(returnDef)> + property="${returnDef.ownerAlias}.${returnDef.ownerProperty}" +<#elseif c2h.isNamedSQLReturnCollection(returnDef)> + role="${returnDef.ownerEntityName}.${returnDef.ownerProperty}" + +<#if returnDef.lockMode?exists> + lock-mode="${returnDef.lockMode.toString().toLowerCase()}" + /> + + + + + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/timestamp.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/timestamp.hbm.ftl new file mode 100644 index 000000000000..dd28000a6b92 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/timestamp.hbm.ftl @@ -0,0 +1,22 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + access="${property.propertyAccessorName}" +<#list property.columns as column> <#-- always only one column, but no direct access method.--> + column="${column.quotedName}" + /> + diff --git a/tooling/hibernate-reveng/src/main/resources/hbm/version.hbm.ftl b/tooling/hibernate-reveng/src/main/resources/hbm/version.hbm.ftl new file mode 100644 index 000000000000..0773fe3bd2ad --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/hbm/version.hbm.ftl @@ -0,0 +1,25 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + access="${property.propertyAccessorName}" + > +<#list property.columns as column> + <#include "column.hbm.ftl"> + + + diff --git a/tooling/hibernate-reveng/src/main/resources/lint/text-report.ftl b/tooling/hibernate-reveng/src/main/resources/lint/text-report.ftl new file mode 100644 index 000000000000..c634b371a3fc --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/lint/text-report.ftl @@ -0,0 +1,18 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#list lintissues as issue> +${issue} + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/Ejb3PropertyGetAnnotation.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/Ejb3PropertyGetAnnotation.ftl new file mode 100644 index 000000000000..125012a00adb --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/Ejb3PropertyGetAnnotation.ftl @@ -0,0 +1,37 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#if ejb3> +<#if pojo.hasIdentifierProperty()> +<#if property.equals(clazz.identifierProperty)> + ${pojo.generateAnnIdGenerator()} +<#-- if this is the id property (getter)--> +<#-- explicitly set the column name for this property--> + + + +<#if c2h.isOneToOne(property)> +${pojo.generateOneToOneAnnotation(property, md)} +<#elseif c2h.isManyToOne(property)> +${pojo.generateManyToOneAnnotation(property)} +<#--TODO support optional and targetEntity--> +${pojo.generateJoinColumnsAnnotation(property, md)} +<#elseif c2h.isCollection(property)> +${pojo.generateCollectionAnnotation(property, md)} +<#else> +${pojo.generateBasicAnnotation(property)} +${pojo.generateAnnColumnAnnotation(property)} + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/Ejb3TypeDeclaration.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/Ejb3TypeDeclaration.ftl new file mode 100644 index 000000000000..396928ccb89b --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/Ejb3TypeDeclaration.ftl @@ -0,0 +1,32 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#if ejb3?if_exists> +<#if pojo.isComponent()> +@${pojo.importType("jakarta.persistence.Embeddable")} +<#else> +@${pojo.importType("jakarta.persistence.Entity")} +@${pojo.importType("jakarta.persistence.Table")}(name="${clazz.table.name}" +<#if clazz.table.schema?exists> + ,schema="${clazz.table.schema}" +<#if clazz.table.catalog?exists> + ,catalog="${clazz.table.catalog}" + +<#assign uniqueConstraint=pojo.generateAnnTableUniqueConstraint()> +<#if uniqueConstraint?has_content> + , uniqueConstraints = ${uniqueConstraint} +) + + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/GetPropertyAnnotation.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/GetPropertyAnnotation.ftl new file mode 100644 index 000000000000..63e0d91871f6 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/GetPropertyAnnotation.ftl @@ -0,0 +1,16 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#include "Ejb3PropertyGetAnnotation.ftl"/> diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/Pojo.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/Pojo.ftl new file mode 100644 index 000000000000..fee889657929 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/Pojo.ftl @@ -0,0 +1,44 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +${pojo.getPackageDeclaration()} +// Generated ${date} by Hibernate Tools ${version} + +<#assign classbody> +<#include "PojoTypeDeclaration.ftl"/> { + +<#if !pojo.isInterface()> +<#include "PojoFields.ftl"/> + +<#include "PojoConstructors.ftl"/> + +<#include "PojoPropertyAccessors.ftl"/> + +<#include "PojoToString.ftl"/> + +<#include "PojoEqualsHashcode.ftl"/> + +<#else> +<#include "PojoInterfacePropertyAccessors.ftl"/> + + +<#include "PojoExtraClassCode.ftl"/> + +} + + +${pojo.generateImports()} +${classbody} + diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/PojoConstructors.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/PojoConstructors.ftl new file mode 100644 index 000000000000..5ce90a4e19cc --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/PojoConstructors.ftl @@ -0,0 +1,41 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> + +<#-- /** default constructor */ --> + public ${pojo.getDeclarationName()}() { + } + +<#if pojo.needsMinimalConstructor()> <#-- /** minimal constructor */ --> + public ${pojo.getDeclarationName()}(${c2j.asParameterList(pojo.getPropertyClosureForMinimalConstructor(), jdk5, pojo)}) { +<#if pojo.isSubclass() && !pojo.getPropertyClosureForSuperclassMinimalConstructor().isEmpty()> + super(${c2j.asArgumentList(pojo.getPropertyClosureForSuperclassMinimalConstructor())}); + +<#list pojo.getPropertiesForMinimalConstructor() as field> + this.${c2j.keyWordCheck(field.name)} = ${c2j.keyWordCheck(field.name)}; + + } + +<#if pojo.needsFullConstructor()> +<#-- /** full constructor */ --> + public ${pojo.getDeclarationName()}(${c2j.asParameterList(pojo.getPropertyClosureForFullConstructor(), jdk5, pojo)}) { +<#if pojo.isSubclass() && !pojo.getPropertyClosureForSuperclassFullConstructor().isEmpty()> + super(${c2j.asArgumentList(pojo.getPropertyClosureForSuperclassFullConstructor())}); + +<#list pojo.getPropertiesForFullConstructor() as field> + this.${c2j.keyWordCheck(field.name)} = ${c2j.keyWordCheck(field.name)}; + + } + diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/PojoEqualsHashcode.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/PojoEqualsHashcode.ftl new file mode 100644 index 000000000000..222343b0c6a9 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/PojoEqualsHashcode.ftl @@ -0,0 +1,33 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#if pojo.needsEqualsHashCode() && !clazz.superclass?exists> +<#assign classNameToCastTo><#if clazz.getProxyInterfaceName?exists>${clazz.getProxyInterfaceName()}<#else>${pojo.getDeclarationName()} + public boolean equals(Object other) { + if ( (this == other ) ) return true; + if ( (other == null ) ) return false; + if ( !(other instanceof ${classNameToCastTo}) ) return false; + ${classNameToCastTo} castOther = ( ${classNameToCastTo} ) other; + + return ${pojo.generateEquals("this", "castOther", jdk5)}; + } + + public int hashCode() { + int result = 17; + +<#list pojo.getAllPropertiesIterator() as property> ${pojo.generateHashCode(property, "result", "this", jdk5)} + return result; + } + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/PojoExtraClassCode.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/PojoExtraClassCode.ftl new file mode 100644 index 000000000000..aa6169aa320d --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/PojoExtraClassCode.ftl @@ -0,0 +1,19 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#if pojo.hasMetaAttribute("class-code")> // The following is extra code specified in the hbm.xml files +${pojo.getExtraClassCode()} + // end of extra code specified in the hbm.xml files + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/PojoFields.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/PojoFields.ftl new file mode 100644 index 000000000000..e2c26218dd35 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/PojoFields.ftl @@ -0,0 +1,23 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#-- // Fields --> + +<#list pojo.getAllPropertiesIterator() as field><#if pojo.getMetaAttribAsBool(field, "gen-property", true)> <#if pojo.hasMetaAttribute(field, "field-description")> /** + ${pojo.getFieldJavaDoc(field, 0)} + */ + ${pojo.getFieldModifiers(field)} ${pojo.getJavaTypeName(field, jdk5)} ${c2j.keyWordCheck(field.name)}<#if pojo.hasFieldInitializor(field, jdk5)> = ${pojo.getFieldInitialization(field, jdk5)}; + + diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/PojoInterfacePropertyAccessors.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/PojoInterfacePropertyAccessors.ftl new file mode 100644 index 000000000000..1fc7ffe5cf3f --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/PojoInterfacePropertyAccessors.ftl @@ -0,0 +1,23 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#-- if interface --> +<#-- Property accessors for interface --> +<#list pojo.getAllPropertiesIterator() as property><#if pojo.getMetaAttribAsBool(property, "gen-property", true)> /** + ${c2j.toJavaDoc(c2j.getMetaAsString(property, "field-description"), 4)} */ + ${pojo.getPropertyGetModifiers(property)} ${pojo.getJavaTypeName(property, jdk5)} ${pojo.getGetterSignature(property)}(); + + ${pojo.getPropertySetModifiers(property)} void set${pojo.getPropertyName(property)}(${pojo.getJavaTypeName(property, jdk5)} ${property.name}); + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/PojoPropertyAccessors.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/PojoPropertyAccessors.ftl new file mode 100644 index 000000000000..92bb357f0015 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/PojoPropertyAccessors.ftl @@ -0,0 +1,33 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#-- // Property accessors --> +<#list pojo.getAllPropertiesIterator() as property> +<#if pojo.getMetaAttribAsBool(property, "gen-property", true)> + <#if pojo.hasFieldJavaDoc(property)> + /** + ${pojo.getFieldJavaDoc(property, 4)} + */ + + <#include "GetPropertyAnnotation.ftl"/> + ${pojo.getPropertyGetModifiers(property)} ${pojo.getJavaTypeName(property, jdk5)} ${pojo.getGetterSignature(property)}() { + return this.${c2j.keyWordCheck(property.name)}; + } + + ${pojo.getPropertySetModifiers(property)} void set${pojo.getPropertyName(property)}(${pojo.getJavaTypeName(property, jdk5)} ${c2j.keyWordCheck(property.name)}) { + this.${c2j.keyWordCheck(property.name)} = ${c2j.keyWordCheck(property.name)}; + } + + diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/PojoToString.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/PojoToString.ftl new file mode 100644 index 000000000000..1822bd8362bc --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/PojoToString.ftl @@ -0,0 +1,29 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +<#if pojo.needsToString()> /** + * toString + * @return String + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(getClass().getName()).append("@").append(Integer.toHexString(hashCode())).append(" ["); +<#list pojo.getToStringPropertiesIterator() as property> buffer.append("${property.getName()}").append("='").append(${pojo.getGetterSignature(property)}()).append("' "); + buffer.append("]"); + + return buffer.toString(); + } + \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/PojoTypeDeclaration.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/PojoTypeDeclaration.ftl new file mode 100644 index 000000000000..d49f824ea66c --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/PojoTypeDeclaration.ftl @@ -0,0 +1,20 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +/** +${pojo.getClassJavaDoc(pojo.getDeclarationName() + " generated by hbm2java", 0)} + */ +<#include "Ejb3TypeDeclaration.ftl"/> +${pojo.getClassModifiers()} ${pojo.getDeclarationType()} ${pojo.getDeclarationName()} ${pojo.getExtendsDeclaration()} ${pojo.getImplementsDeclaration()} \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/main/resources/pojo/test.ftl b/tooling/hibernate-reveng/src/main/resources/pojo/test.ftl new file mode 100644 index 000000000000..68bcfad8756c --- /dev/null +++ b/tooling/hibernate-reveng/src/main/resources/pojo/test.ftl @@ -0,0 +1,20 @@ +<#-- +~ Copyright 2010 - 2025 Red Hat, Inc. +~ +~ 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. +--> +/** +${pojo.getClassJavaDoc(render.eval(ctx, pojo.getDeclarationName() + " generated by hbm2java"), 0)} + */ +${templates.Ejb3TypeDeclaration} +${pojo.getClassModifiers()} ${pojo.getDeclarationType()} ${pojo.getDeclarationName()} ${pojo.getExtendsDeclaration()} ${pojo.getImplementsDeclaration()} \ No newline at end of file diff --git a/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/core/ReverseEngineeringStrategyFactoryTest.java b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/core/ReverseEngineeringStrategyFactoryTest.java new file mode 100644 index 000000000000..f292eefe0a69 --- /dev/null +++ b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/core/ReverseEngineeringStrategyFactoryTest.java @@ -0,0 +1,53 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2018-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.core; + +import org.hibernate.tool.reveng.internal.core.strategy.DefaultStrategy; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + + +public class ReverseEngineeringStrategyFactoryTest { + + @Test + public void testCreateReverseEngineeringStrategy() { + RevengStrategy reverseEngineeringStrategy = + RevengStrategyFactory.createReverseEngineeringStrategy(); + assertNotNull(reverseEngineeringStrategy); + assertEquals( + DefaultStrategy.class.getName(), + reverseEngineeringStrategy.getClass().getName()); + reverseEngineeringStrategy = + RevengStrategyFactory.createReverseEngineeringStrategy( + TestReverseEngineeringStrategyFactory.class.getName()); + assertNotNull(reverseEngineeringStrategy); + assertEquals( + TestReverseEngineeringStrategyFactory.class.getName(), + reverseEngineeringStrategy.getClass().getName()); + reverseEngineeringStrategy = + RevengStrategyFactory.createReverseEngineeringStrategy(null); + assertEquals( + DefaultStrategy.class.getName(), + reverseEngineeringStrategy.getClass().getName()); + } + + public static class TestReverseEngineeringStrategyFactory extends DefaultStrategy {} + +} diff --git a/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/export/ExporterFactoryTest.java b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/export/ExporterFactoryTest.java new file mode 100644 index 000000000000..11e98ad25410 --- /dev/null +++ b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/export/ExporterFactoryTest.java @@ -0,0 +1,53 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2018-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.export; + +import org.hibernate.tool.reveng.internal.export.common.AbstractExporter; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +public class ExporterFactoryTest { + + @Test + public void testCreateExporter() { + try { + ExporterFactory.createExporter("foobar"); + fail(); + } catch(Throwable t) { + assertTrue(t.getMessage().contains("foobar")); + } + Exporter exporter = ExporterFactory.createExporter( + "org.hibernate.tool.reveng.api.export.ExporterFactoryTest$TestExporter"); + assertNotNull(exporter); + assertTrue(exporter instanceof TestExporter); + exporter = ExporterFactory.createExporter(ExporterType.JAVA); + assertNotNull(exporter); + assertEquals( + ExporterType.JAVA.className(), + exporter.getClass().getName()); + } + + public static class TestExporter extends AbstractExporter { + @Override protected void doStart() {} + } + +} diff --git a/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/export/ExporterTypeTest.java b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/export/ExporterTypeTest.java new file mode 100644 index 000000000000..26c9685e4308 --- /dev/null +++ b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/export/ExporterTypeTest.java @@ -0,0 +1,36 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2018-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.export; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ExporterTypeTest { + + @Test + public void testExporterType() { + assertEquals( + "org.hibernate.tool.reveng.internal.export.java.JavaExporter", + ExporterType.JAVA.className()); + assertEquals( + "org.hibernate.tool.reveng.internal.export.cfg.CfgExporter", + ExporterType.CFG.className()); + } + +} diff --git a/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/version/VersionTest.java b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/version/VersionTest.java new file mode 100644 index 000000000000..e4d7e294edc6 --- /dev/null +++ b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/version/VersionTest.java @@ -0,0 +1,14 @@ +package org.hibernate.tool.reveng.api.version; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class VersionTest { + + @Test + public void testSomething() { + assertEquals( org.hibernate.Version.getVersionString(), Version.versionString() ); + } + +} diff --git a/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinterTest.java b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinterTest.java new file mode 100644 index 000000000000..35a0fb3d44a3 --- /dev/null +++ b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/api/xml/XMLPrettyPrinterTest.java @@ -0,0 +1,79 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2024-2025 Red Hat, Inc. + * + * 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.tool.reveng.api.xml; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.io.PrintWriter; +import java.nio.file.Files; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class XMLPrettyPrinterTest { + + private static final String XML_BEFORE = "foobar"; + + private static final String XML_AFTER = + "\n" + + "\n" + + " foobar\n" + + "\n"; + + private static final String XML_COMMENT = ""; + + private static final String fileName = "foobarfile.xml"; + + @TempDir + private File tempDir; + + private File xmlFile = null; + + @BeforeEach + public void beforeEach() throws Exception { + xmlFile = new File(tempDir, fileName); + PrintWriter writer = new PrintWriter(xmlFile); + writer.print(XML_BEFORE); + writer.flush(); + writer.close(); + } + + @Test + public void testXmlPrettyPrintDefault() throws Exception { + XMLPrettyPrinter.prettyPrintFile(xmlFile); + String result = Files.readString(xmlFile.toPath()); + assertEquals(XML_AFTER, result); + } + + @Test + public void testXmlPrettyPrintWithStrategy() throws Exception { + XMLPrettyPrinter.prettyPrintFile(xmlFile, new FooBarStrategy()); + String result = Files.readString(xmlFile.toPath()); + assertEquals(XML_AFTER + XML_COMMENT, result); + } + + public static class FooBarStrategy implements XMLPrettyPrinterStrategy { + @Override + public String prettyPrint(String xml) throws Exception { + return XML_AFTER + XML_COMMENT; + } + } + +} diff --git a/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/core/binder/TypeUtilsTest.java b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/core/binder/TypeUtilsTest.java new file mode 100644 index 000000000000..99fda6dd350d --- /dev/null +++ b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/core/binder/TypeUtilsTest.java @@ -0,0 +1,22 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2019-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.binder; + +public class TypeUtilsTest { + +} diff --git a/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/core/strategy/MetaAttributeHelperTest.java b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/core/strategy/MetaAttributeHelperTest.java new file mode 100644 index 000000000000..365aeddc371c --- /dev/null +++ b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/core/strategy/MetaAttributeHelperTest.java @@ -0,0 +1,57 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2020-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.core.strategy; + +import org.apache.commons.collections4.MultiValuedMap; +import org.hibernate.tool.reveng.internal.core.strategy.MetaAttributeHelper.SimpleMetaAttribute; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.ByteArrayInputStream; +import java.util.Collection; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MetaAttributeHelperTest { + + private static String XML = + " " + + " foo" + + " "; + + @Test + public void testLoadMetaMap() throws Exception { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document document = db.parse(new ByteArrayInputStream(XML.getBytes())); + MultiValuedMap mm = MetaAttributeHelper.loadMetaMap(document.getDocumentElement()); + assertEquals(1, mm.size()); + Collection attributeList = mm.get("blah"); + assertEquals(1, attributeList.size()); + Optional first = attributeList.stream().findFirst(); + assertTrue(first.isPresent()); + SimpleMetaAttribute attribute = first.get(); + assertEquals(true, attribute.inheritable); + assertEquals("foo", attribute.value); + } + +} diff --git a/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/util/ReflectionUtilTest.java b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/util/ReflectionUtilTest.java new file mode 100644 index 000000000000..8a939804fc14 --- /dev/null +++ b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/util/ReflectionUtilTest.java @@ -0,0 +1,42 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2004-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.util; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +public class ReflectionUtilTest { + + @Test + public void testClassForName() throws Exception { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + assertNotNull(contextClassLoader); + try { + Class clazz = ReflectionUtil.classForName("org.hibernate.tool.reveng.internal.util.ReflectionUtil"); + assertSame(ReflectionUtil.class, clazz); + Thread.currentThread().setContextClassLoader(null); + clazz = ReflectionUtil.classForName("org.hibernate.tool.reveng.internal.util.ReflectionUtil"); + assertSame(ReflectionUtil.class, clazz); + } finally { + Thread.currentThread().setContextClassLoader(contextClassLoader); + } + } + +} diff --git a/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/util/StringUtilTest.java b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/util/StringUtilTest.java new file mode 100644 index 000000000000..a1d4cb10f061 --- /dev/null +++ b/tooling/hibernate-reveng/src/test/java/org/hibernate/tool/reveng/internal/util/StringUtilTest.java @@ -0,0 +1,34 @@ +/* + * Hibernate Tools, Tooling for your Hibernate Projects + * + * Copyright 2004-2025 Red Hat, Inc. + * + * 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.tool.reveng.internal.util; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class StringUtilTest { + + @Test + public void testIsEmptyOrNull() { + assertTrue(StringUtil.isEmptyOrNull(null)); + assertTrue(StringUtil.isEmptyOrNull("")); + assertFalse(StringUtil.isEmptyOrNull("foo")); + } + +} From 8ac5ecc5115c7a10b1652192eeb8d0661a30b550 Mon Sep 17 00:00:00 2001 From: Koen Aers Date: Tue, 4 Nov 2025 15:01:49 +0100 Subject: [PATCH 2/2] HHH-19879: Move Hibernate Tools' reveng module to Hibernate ORM and merge the relevant ant/gradle/maven plugins - Add the reveng Ant tasks Signed-off-by: Koen Aers --- tooling/hibernate-ant/hibernate-ant.gradle | 1 + .../hibernate/tool/ant/ConfigurationTask.java | 134 ++++ .../org/hibernate/tool/ant/ExporterTask.java | 101 +++ .../tool/ant/GenericExporterTask.java | 80 +++ .../tool/ant/Hbm2CfgXmlExporterTask.java | 37 + .../tool/ant/Hbm2DAOExporterTask.java | 33 + .../tool/ant/Hbm2DDLExporterTask.java | 116 ++++ .../tool/ant/Hbm2DocExporterTask.java | 24 + .../tool/ant/Hbm2HbmXmlExporterTask.java | 24 + .../tool/ant/Hbm2JavaExporterTask.java | 47 ++ .../tool/ant/HbmLintExporterTask.java | 26 + .../hibernate/tool/ant/HibernateToolTask.java | 293 ++++++++ .../tool/ant/JDBCConfigurationTask.java | 133 ++++ .../tool/ant/JPAConfigurationTask.java | 67 ++ .../hibernate/tool/ant/JavaFormatterTask.java | 100 +++ .../hibernate/tool/ant/QueryExporterTask.java | 104 +++ .../tool/ant/util/ExceptionUtil.java | 48 ++ .../hibernate-reveng/hibernate-reveng.gradle | 2 +- .../hibernate/tool/hbm2ddl/SchemaExport.java | 638 ++++++++++++++++++ .../hibernate/tool/hbm2ddl/SchemaUpdate.java | 321 +++++++++ .../org/hibernate/tool/hbm2ddl/Target.java | 46 ++ .../tool/hbm2ddl/TargetTypeHelper.java | 53 ++ 22 files changed, 2427 insertions(+), 1 deletion(-) create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/ConfigurationTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/ExporterTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/GenericExporterTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2CfgXmlExporterTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DAOExporterTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DDLExporterTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DocExporterTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2HbmXmlExporterTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2JavaExporterTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/HbmLintExporterTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/HibernateToolTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JDBCConfigurationTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JPAConfigurationTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JavaFormatterTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/QueryExporterTask.java create mode 100644 tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/util/ExceptionUtil.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdate.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/Target.java create mode 100644 tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/TargetTypeHelper.java diff --git a/tooling/hibernate-ant/hibernate-ant.gradle b/tooling/hibernate-ant/hibernate-ant.gradle index a6af63b9a823..1ddf1e3e5229 100644 --- a/tooling/hibernate-ant/hibernate-ant.gradle +++ b/tooling/hibernate-ant/hibernate-ant.gradle @@ -10,5 +10,6 @@ description = 'Annotation Processor to generate JPA 2 static metamodel classes' dependencies { compileOnly libs.ant implementation project( ':hibernate-core' ) + implementation project( ':hibernate-reveng') testImplementation libs.ant } \ No newline at end of file diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/ConfigurationTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/ConfigurationTask.java new file mode 100644 index 000000000000..c0d8c77c065a --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/ConfigurationTask.java @@ -0,0 +1,134 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.FileSet; +import org.hibernate.tool.reveng.api.metadata.MetadataDescriptor; +import org.hibernate.tool.reveng.api.metadata.MetadataDescriptorFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; + +/** + * @author max + * + */ +public class ConfigurationTask extends Task { + + List fileSets = new ArrayList(); + MetadataDescriptor metadataDescriptor; + File configurationFile; + File propertyFile; + protected String entityResolver; + + public ConfigurationTask() { + setDescription( "Standard Configuration" ); + } + + public void addConfiguredFileSet(FileSet fileSet) { + fileSets.add( fileSet ); + } + + public final MetadataDescriptor getMetadataDescriptor() { + if ( metadataDescriptor == null ) { + metadataDescriptor = createMetadataDescriptor(); + } + return metadataDescriptor; + } + + protected MetadataDescriptor createMetadataDescriptor() { + return MetadataDescriptorFactory + .createNativeDescriptor( + configurationFile, + getFiles(), + loadPropertiesFile() ); + } + + protected Properties loadPropertiesFile() { + if ( propertyFile != null ) { + Properties properties = new Properties(); // TODO: should we "inherit" from the ant projects properties ? + try (FileInputStream is = new FileInputStream( propertyFile )) { + properties.load( is ); + return properties; + } + catch (FileNotFoundException e) { + throw new BuildException( propertyFile + " not found.", e ); + } + catch (IOException e) { + throw new BuildException( "Problem while loading " + propertyFile, e ); + } + } + else { + return null; + } + } + + + protected File[] getFiles() { + + List files = new LinkedList(); + for ( FileSet fs : fileSets ) { + + DirectoryScanner ds = fs.getDirectoryScanner( getProject() ); + + String[] dsFiles = ds.getIncludedFiles(); + for ( String dsFile : dsFiles ) { + File f = new File( dsFile ); + if ( !f.isFile() ) { + f = new File( ds.getBasedir(), dsFile ); + } + + files.add( f ); + } + } + + return files.toArray( new File[0] ); + } + + + public File getConfigurationFile() { + return configurationFile; + } + + public void setConfigurationFile(File configurationFile) { + this.configurationFile = configurationFile; + } + + public File getPropertyFile() { + return propertyFile; + } + + public void setPropertyFile(File propertyFile) { + this.propertyFile = propertyFile; + } + + public void setEntityResolver(String entityResolverName) { + this.entityResolver = entityResolverName; + } + + public void setNamingStrategy(String namingStrategy) { + log("setting unused naming strategy: " + namingStrategy); + } + + @Override + public Object clone() throws CloneNotSupportedException { + ConfigurationTask ct = (ConfigurationTask) super.clone(); + ct.fileSets.addAll( this.fileSets ); + ct.metadataDescriptor = this.metadataDescriptor; + ct.propertyFile = this.propertyFile; + ct.entityResolver = this.entityResolver; + return ct; + } + +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/ExporterTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/ExporterTask.java new file mode 100644 index 000000000000..4f4eedef713e --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/ExporterTask.java @@ -0,0 +1,101 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Environment; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.PropertySet; +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterConstants; + +import java.io.File; +import java.util.Properties; + +/** + * @author max + * + * Is not actually a ant task, but simply just a task part of a HibernateToolTask + * + */ +public abstract class ExporterTask { + + // refactor out so not dependent on Ant ? + protected HibernateToolTask parent; + Properties properties; + private Path templatePath; + + public ExporterTask(HibernateToolTask parent) { + this.parent = parent; + this.properties = new Properties(); + } + + + /*final*/ public void execute() { + + Exporter exporter = configureExporter(createExporter() ); + exporter.start(); + + } + + protected abstract Exporter createExporter(); + + public File getDestdir() { + File destdir = (File)this.properties.get(ExporterConstants.DESTINATION_FOLDER); + if(destdir==null) { + return parent.getDestDir(); + } + else { + return destdir; + } + } + public void setDestdir(File destdir) { + this.properties.put(ExporterConstants.DESTINATION_FOLDER, destdir); + } + + public void setTemplatePath(Path path) { + templatePath = path; + } + + public void setTemplatePrefix(String s) { + } + + public void validateParameters() { + if(getDestdir()==null) { + throw new BuildException("destdir must be set, either locally or on "); + } + } + + public void addConfiguredPropertySet(PropertySet ps) { + properties.putAll(ps.getProperties()); + } + + public void addConfiguredProperty(Environment.Variable property) { + properties.put(property.getKey(), property.getValue()); + } + + protected Path getTemplatePath() { + if(templatePath==null) { + return parent.getTemplatePath(); + } + else { + return templatePath; + } + } + + + abstract String getName(); + + protected Exporter configureExporter(Exporter exporter) { + Properties prop = new Properties(); + prop.putAll(parent.getProperties()); + prop.putAll(properties); + exporter.getProperties().putAll(prop); + exporter.getProperties().put(ExporterConstants.METADATA_DESCRIPTOR, parent.getMetadataDescriptor()); + exporter.getProperties().put(ExporterConstants.DESTINATION_FOLDER, getDestdir()); + exporter.getProperties().put(ExporterConstants.TEMPLATE_PATH, getTemplatePath().list()); + return exporter; + } +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/GenericExporterTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/GenericExporterTask.java new file mode 100644 index 000000000000..9b672d29a9c2 --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/GenericExporterTask.java @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterConstants; +import org.hibernate.tool.reveng.api.export.ExporterFactory; +import org.hibernate.tool.reveng.api.export.ExporterType; + +/** + * @author max + * + */ +public class GenericExporterTask extends ExporterTask { + + public GenericExporterTask(HibernateToolTask parent) { + super(parent); + } + + String templateName; + String exporterClass; + String filePattern; + String forEach; + + /** + * The FilePattern defines the pattern used to generate files. + * @param filePattern + */ + public void setFilePattern(String filePattern) { + this.filePattern = filePattern; + } + + public void setForEach(String forEach) { + this.forEach = forEach; + } + + public void setTemplate(String templateName) { + this.templateName = templateName; + } + + public void setExporterClass(String exporterClass) { + this.exporterClass = exporterClass; + } + + protected Exporter createExporter() { + if (exporterClass == null) { + return ExporterFactory.createExporter(ExporterType.GENERIC); + } + else { + return ExporterFactory.createExporter(exporterClass); + } + } + + protected Exporter configureExporter(Exporter exp) { + super.configureExporter(exp); + if (templateName != null) { + exp.getProperties().put(ExporterConstants.TEMPLATE_NAME, templateName); + } + if (filePattern != null) { + exp.getProperties().put(ExporterConstants.FILE_PATTERN, filePattern); + } + if (forEach != null) { + exp.getProperties().put(ExporterConstants.FOR_EACH, forEach); + } + return exp; + } + + public String getName() { + StringBuffer buf = new StringBuffer("generic exporter"); + if(exporterClass!=null) { + buf.append( "class: " + exporterClass); + } + if(templateName!=null) { + buf.append( "template: " + templateName); + } + return buf.toString(); + } +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2CfgXmlExporterTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2CfgXmlExporterTask.java new file mode 100644 index 000000000000..2b79d9cdb0fe --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2CfgXmlExporterTask.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterFactory; +import org.hibernate.tool.reveng.api.export.ExporterType; + +public class Hbm2CfgXmlExporterTask extends ExporterTask { + + private boolean ejb3; + + public Hbm2CfgXmlExporterTask(HibernateToolTask parent) { + super(parent); + } + + public Exporter createExporter() { + return ExporterFactory.createExporter(ExporterType.CFG); + } + + public void setEjb3(boolean ejb3) { + this.ejb3 = ejb3; + } + + public String getName() { + return "hbm2cfgxml (Generates hibernate.cfg.xml)"; + } + + protected Exporter configureExporter(Exporter exporter) { + super.configureExporter( exporter ); + exporter.getProperties().setProperty("ejb3", ""+ejb3); + return exporter; + } + +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DAOExporterTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DAOExporterTask.java new file mode 100644 index 000000000000..af777e9c1ec1 --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DAOExporterTask.java @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterConstants; +import org.hibernate.tool.reveng.api.export.ExporterFactory; +import org.hibernate.tool.reveng.api.export.ExporterType; + +/** + * @author Dennis Byrne + */ +public class Hbm2DAOExporterTask extends Hbm2JavaExporterTask { + + public Hbm2DAOExporterTask(HibernateToolTask parent) { + super(parent); + } + + protected Exporter createExporter() { + Exporter result = ExporterFactory.createExporter(ExporterType.DAO); + result.getProperties().putAll(parent.getProperties()); + result.getProperties().put(ExporterConstants.METADATA_DESCRIPTOR, parent.getMetadataDescriptor()); + result.getProperties().put(ExporterConstants.DESTINATION_FOLDER, getDestdir()); + return result; + } + + public String getName() { + return "hbm2dao (Generates a set of DAOs)"; + } + +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DDLExporterTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DDLExporterTask.java new file mode 100644 index 000000000000..d1aa3484af8e --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DDLExporterTask.java @@ -0,0 +1,116 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterConstants; +import org.hibernate.tool.reveng.api.export.ExporterFactory; +import org.hibernate.tool.reveng.api.export.ExporterType; + +/** + * @author max + * + */ +public class Hbm2DDLExporterTask extends ExporterTask { + + boolean exportToDatabase = true; + boolean scriptToConsole = true; + boolean schemaUpdate = false; + String delimiter = ";"; + boolean drop = false; + boolean create = true; + boolean format = false; + + String outputFileName = null; + private boolean haltOnError = false; + + public Hbm2DDLExporterTask(HibernateToolTask parent) { + super(parent); + } + + public String getName() { + return "hbm2ddl (Generates database schema)"; + } + + protected Exporter configureExporter(Exporter exp) { + Exporter exporter = super.configureExporter( exp ); + exporter.getProperties().put(ExporterConstants.EXPORT_TO_DATABASE, exportToDatabase); + exporter.getProperties().put(ExporterConstants.EXPORT_TO_CONSOLE, scriptToConsole); + exporter.getProperties().put(ExporterConstants.SCHEMA_UPDATE, schemaUpdate); + exporter.getProperties().put(ExporterConstants.DELIMITER, delimiter); + exporter.getProperties().put(ExporterConstants.DROP_DATABASE, drop); + exporter.getProperties().put(ExporterConstants.CREATE_DATABASE, create); + exporter.getProperties().put(ExporterConstants.FORMAT, format); + if (outputFileName == null) { + exporter.getProperties().remove(ExporterConstants.OUTPUT_FILE_NAME); + } + else { + exporter.getProperties().put(ExporterConstants.OUTPUT_FILE_NAME, outputFileName); + } + exporter.getProperties().put(ExporterConstants.HALT_ON_ERROR, haltOnError); + return exporter; + } + + protected Exporter createExporter() { + Exporter exporter = ExporterFactory.createExporter(ExporterType.DDL); + exporter.getProperties().putAll(parent.getProperties()); + exporter.getProperties().put(ExporterConstants.METADATA_DESCRIPTOR, parent.getProperties()); + exporter.getProperties().put(ExporterConstants.DESTINATION_FOLDER, parent.getDestDir()); + return exporter; + } + + + public void setExport(boolean export) { + exportToDatabase = export; + } + + /** + * Run SchemaUpdate instead of SchemaExport + */ + public void setUpdate(boolean update) { + this.schemaUpdate = update; + } + + /** + * Output sql to console ? (default true) + */ + public void setConsole(boolean console) { + this.scriptToConsole = console; + } + + /** + * Format the generated sql + */ + public void setFormat(boolean format) { + this.format = format; + } + + /** + * File out put name (default: empty) + */ + public void setOutputFileName(String fileName) { + outputFileName = fileName; + } + + public void setDrop(boolean drop) { + this.drop = drop; + } + + public void setCreate(boolean create) { + this.create = create; + } + + public void setDelimiter(String delimiter) { + this.delimiter = delimiter; + } + + public String getDelimiter() { + return delimiter; + } + + public void setHaltonerror(boolean haltOnError) { + this.haltOnError = haltOnError; + } +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DocExporterTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DocExporterTask.java new file mode 100644 index 000000000000..9b42253f065c --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2DocExporterTask.java @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterFactory; +import org.hibernate.tool.reveng.api.export.ExporterType; + +public class Hbm2DocExporterTask extends ExporterTask { + + public Hbm2DocExporterTask(HibernateToolTask parent) { + super(parent); + } + + public String getName() { + return "hbm2doc (Generates html schema documentation)"; + } + + protected Exporter createExporter() { + return ExporterFactory.createExporter(ExporterType.DOC); + } +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2HbmXmlExporterTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2HbmXmlExporterTask.java new file mode 100644 index 000000000000..6cf1e96d175b --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2HbmXmlExporterTask.java @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterFactory; +import org.hibernate.tool.reveng.api.export.ExporterType; + +public class Hbm2HbmXmlExporterTask extends ExporterTask { + + public Hbm2HbmXmlExporterTask(HibernateToolTask parent) { + super(parent); + } + + protected Exporter createExporter() { + return ExporterFactory.createExporter(ExporterType.HBM); + } + + public String getName() { + return "hbm2hbmxml (Generates a set of hbm.xml files)"; + } +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2JavaExporterTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2JavaExporterTask.java new file mode 100644 index 000000000000..02027228de1d --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/Hbm2JavaExporterTask.java @@ -0,0 +1,47 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterFactory; +import org.hibernate.tool.reveng.api.export.ExporterType; + +/** + * @author max + * + */ +public class Hbm2JavaExporterTask extends ExporterTask { + + boolean ejb3 = true; + + boolean jdk5 = true; + + public Hbm2JavaExporterTask(HibernateToolTask parent) { + super( parent ); + } + + public void setEjb3(boolean b) { + ejb3 = b; + } + + public void setJdk5(boolean b) { + jdk5 = b; + } + + protected Exporter configureExporter(Exporter exp) { + super.configureExporter( exp ); + exp.getProperties().setProperty("ejb3", ""+ejb3); + exp.getProperties().setProperty("jdk5", ""+jdk5); + return exp; + } + + protected Exporter createExporter() { + return ExporterFactory.createExporter(ExporterType.JAVA); + } + + public String getName() { + return "hbm2java (Generates a set of .java files)"; + } +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/HbmLintExporterTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/HbmLintExporterTask.java new file mode 100644 index 000000000000..829111aebdf1 --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/HbmLintExporterTask.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterFactory; +import org.hibernate.tool.reveng.api.export.ExporterType; + +public class HbmLintExporterTask extends ExporterTask { + + public HbmLintExporterTask(HibernateToolTask parent) { + super( parent ); + } + + protected Exporter createExporter() { + return ExporterFactory.createExporter(ExporterType.HBM_LINT); + } + + + String getName() { + return "hbmlint (scans mapping for errors)"; + } + +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/HibernateToolTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/HibernateToolTask.java new file mode 100644 index 000000000000..4af8a181eae8 --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/HibernateToolTask.java @@ -0,0 +1,293 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.Environment; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.PropertySet; +import org.hibernate.tool.ant.util.ExceptionUtil; +import org.hibernate.tool.reveng.api.metadata.MetadataDescriptor; +import org.hibernate.tool.reveng.internal.util.StringUtil; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +/** + * @author max + * + */ +public class HibernateToolTask extends Task { + + ConfigurationTask configurationTask; + File destDir; + List generators = new ArrayList(); + Path classPath; + Path templatePath; + Properties properties = new Properties(); + + public HibernateToolTask() { + super(); + } + + private void checkConfiguration() { + if(configurationTask!=null) { + throw new BuildException("Only a single configuration is allowed."); + } + } + + public ConfigurationTask createConfiguration() { + checkConfiguration(); + configurationTask = new ConfigurationTask(); + return configurationTask; + } + + + public JDBCConfigurationTask createJDBCConfiguration() { + checkConfiguration(); + configurationTask = new JDBCConfigurationTask(); + return (JDBCConfigurationTask) configurationTask; + } + + public JPAConfigurationTask createJpaConfiguration() { + checkConfiguration(); + configurationTask = new JPAConfigurationTask(); + return (JPAConfigurationTask) configurationTask; + } + + public ExporterTask createHbm2DDL() { + ExporterTask generator = new Hbm2DDLExporterTask(this); + addGenerator( generator ); + return generator; + } + + public ExporterTask createHbmTemplate() { + ExporterTask generator = new GenericExporterTask(this); + addGenerator( generator ); + return generator; + } + + public ExporterTask createHbm2CfgXml() { + ExporterTask generator = new Hbm2CfgXmlExporterTask(this); + addGenerator( generator ); + + return generator; + } + + protected void addGenerator(ExporterTask generator) { + generators.add(generator); + } + + public ExporterTask createHbm2Java() { + ExporterTask generator = new Hbm2JavaExporterTask(this); + addGenerator( generator ); + return generator; + } + + public ExporterTask createHbm2HbmXml() { + ExporterTask generator= new Hbm2HbmXmlExporterTask(this); + addGenerator( generator ); + return generator; + } + + public ExporterTask createHbm2Doc() { + ExporterTask generator= new Hbm2DocExporterTask(this); + addGenerator( generator ); + return generator; + } + + /*public ExporterTask createHbm2Jsf(){ + ExporterTask generator= new Hbm2JsfGeneratorTask(this); + generators.add(generator); + return generator; + }*/ + + public ExporterTask createHbm2DAO(){ + ExporterTask generator= new Hbm2DAOExporterTask(this); + addGenerator( generator ); + return generator; + } + + + public QueryExporterTask createQuery() { + QueryExporterTask generator = new QueryExporterTask(this); + generators.add(generator); + return generator; + } + + public HbmLintExporterTask createHbmLint() { + HbmLintExporterTask generator = new HbmLintExporterTask(this); + generators.add(generator); + return generator; + } + + + /** + * Set the classpath to be used when running the Java class + * + * @param s an Ant Path object containing the classpath. + */ + public void setClasspath(Path s) { + classPath = s; + } + + + /** + * Adds a path to the classpath. + * + * @return created classpath + */ + public Path createClasspath() { + classPath = new Path(getProject() ); + return classPath; + } + + + public void execute() { + if(configurationTask==null) { + throw new BuildException("No configuration specified. <" + getTaskName() + "> must have one of the following: , , or "); + } + log("Executing Hibernate Tool with a " + configurationTask.getDescription() ); + validateParameters(); + Iterator iterator = generators.iterator(); + + AntClassLoader loader = getProject().createClassLoader(classPath); + + ExporterTask generatorTask = null; + int count = 1; + try { + ClassLoader classLoader = this.getClass().getClassLoader(); + loader.setParent(classLoader ); // if this is not set, classes from the taskdef cannot be found - which is crucial for e.g. annotations. + loader.setThreadContextLoader(); + + while (iterator.hasNext() ) { + generatorTask = iterator.next(); + log(count++ + ". task: " + generatorTask.getName() ); + generatorTask.execute(); + } + } + catch (RuntimeException re) { + reportException(re, count, generatorTask); + } + finally { + if (loader != null) { + loader.resetThreadContextLoader(); + loader.cleanup(); + } + } + } + + private void reportException(Throwable re, int count, ExporterTask generatorTask) { + log("An exception occurred while running exporter #" + count + ":" + generatorTask, Project.MSG_ERR); + log("To get the full stack trace run ant with -verbose", Project.MSG_ERR); + + log(re.toString(), Project.MSG_ERR); + StringBuilder ex = new StringBuilder(); + Throwable cause = re.getCause(); + while(cause!=null) { + ex.append(cause.toString()).append("\n"); + if(cause==cause.getCause()) { + break; // we reached the top. + } + else { + cause=cause.getCause(); + } + } + if(!StringUtil.isEmptyOrNull(ex.toString())) { + log(ex.toString(), Project.MSG_ERR); + } + + String newbieMessage = ExceptionUtil.getProblemSolutionOrCause(re); + if(newbieMessage!=null) { + log(newbieMessage); + } + + if(re instanceof BuildException) { + throw (BuildException)re; + } + else { + throw new BuildException(re, getLocation()); + } + } + + private void validateParameters() { + if(generators.isEmpty()) { + throw new BuildException("No exporters specified in . There has to be at least one specified. An exporter is e.g. or . See documentation for details.", getLocation()); + } + else { + for (ExporterTask generatorTask : generators) { + generatorTask.validateParameters(); + } + } + } + + public File getDestDir() { + return destDir; + } + + public void setDestDir(File file) { + destDir = file; + } + + public MetadataDescriptor getMetadataDescriptor() { + return configurationTask.getMetadataDescriptor(); + } + + public void setTemplatePath(Path path) { + templatePath = path; + } + + public Path getTemplatePath() { + if(templatePath==null) { + templatePath = new Path(getProject()); // empty path + } + return templatePath; + } + + public Properties getProperties() { + Properties p = new Properties(); + p.putAll(getMetadataDescriptor().getProperties()); + p.putAll(properties); + return p; + } + + public void addConfiguredPropertySet(PropertySet ps) { + properties.putAll(ps.getProperties()); + } + + public void addConfiguredProperty(Environment.Variable property) { + String key = property.getKey(); + String value = property.getValue(); + if (key==null) { + log( "Ignoring unnamed task property", Project.MSG_WARN ); + return; + } + if (value==null){ + //This is legal in ANT, make sure we warn properly: + log( "Ignoring task property '" +key+"' as no value was specified", Project.MSG_WARN ); + return; + } + properties.put( key, value ); + } + + @Override + public Object clone() throws CloneNotSupportedException { + HibernateToolTask htt = (HibernateToolTask) super.clone(); + htt.configurationTask = this.configurationTask; + htt.destDir = this.destDir; + htt.generators.addAll(this.generators); + htt.classPath = this.classPath; + htt.templatePath = this.templatePath; + htt.properties.putAll(this.properties); + return htt; + } + +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JDBCConfigurationTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JDBCConfigurationTask.java new file mode 100644 index 000000000000..67509c315931 --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JDBCConfigurationTask.java @@ -0,0 +1,133 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.apache.tools.ant.types.Path; +import org.hibernate.boot.cfgxml.internal.ConfigLoader; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.tool.reveng.api.metadata.MetadataConstants; +import org.hibernate.tool.reveng.api.metadata.MetadataDescriptor; +import org.hibernate.tool.reveng.api.metadata.MetadataDescriptorFactory; +import org.hibernate.tool.reveng.api.core.RevengSettings; +import org.hibernate.tool.reveng.api.core.RevengStrategy; +import org.hibernate.tool.reveng.api.core.RevengStrategyFactory; + +import java.io.File; +import java.util.Map; +import java.util.Properties; + + +/** + * @author max + * @author Alexandru Popescu + */ +public class JDBCConfigurationTask extends ConfigurationTask { + + boolean preferBasicCompositeIds = true; + + String reverseEngineeringStrategyClass; + String packageName; + Path revengFiles; + + boolean detectOneToOne = true; + boolean detectManyToMany = true; + boolean detectOptimisticLock = true; + + public JDBCConfigurationTask() { + setDescription("JDBC Configuration (for reverse engineering)"); + } + protected MetadataDescriptor createMetadataDescriptor() { + Properties properties = loadProperties(); + RevengStrategy res = createReverseEngineeringStrategy(); + properties.put(MetadataConstants.PREFER_BASIC_COMPOSITE_IDS, preferBasicCompositeIds); + return MetadataDescriptorFactory + .createReverseEngineeringDescriptor( + res, + properties); + } + + private RevengStrategy createReverseEngineeringStrategy() { + File[] revengFileList = null; + if (revengFiles != null ) { + String[] fileNames = revengFiles.list(); + revengFileList = new File[fileNames.length]; + for (int i = 0; i < fileNames.length; i++) { + revengFileList[i] = new File(fileNames[i]); + } + } + + RevengStrategy strategy = + RevengStrategyFactory.createReverseEngineeringStrategy( + reverseEngineeringStrategyClass, revengFileList); + + RevengSettings qqsettings = + new RevengSettings(strategy).setDefaultPackageName(packageName) + .setDetectManyToMany( detectManyToMany ) + .setDetectOneToOne( detectOneToOne ) + .setDetectOptimisticLock( detectOptimisticLock ); + + strategy.setSettings(qqsettings); + + return strategy; + } + + + public void setPackageName(String pkgName) { + packageName = pkgName; + } + + public void setReverseStrategy(String fqn) { + reverseEngineeringStrategyClass = fqn; + } + + public void setRevEngFile(Path p) { + revengFiles = p; + } + + public void setPreferBasicCompositeIds(boolean b) { + preferBasicCompositeIds = b; + } + + public void setDetectOneToOne(boolean b) { + detectOneToOne = b; + } + + public void setDetectManyToMany(boolean b) { + detectManyToMany = b; + } + + public void setDetectOptimisticLock(boolean b) { + detectOptimisticLock = b; + } + + private Map loadCfgXmlFile() { + return new ConfigLoader(new BootstrapServiceRegistryBuilder().build()) + .loadConfigXmlFile(getConfigurationFile()) + .getConfigurationValues(); + } + + private Properties loadProperties() { + Properties result = new Properties(); + if (getPropertyFile() != null) { + result.putAll(loadPropertiesFile()); + } + if (getConfigurationFile() != null) { + result.putAll(loadCfgXmlFile()); + } + return result; + } + + public Object clone() throws CloneNotSupportedException { + JDBCConfigurationTask jct = (JDBCConfigurationTask) super.clone(); + jct.preferBasicCompositeIds = this.preferBasicCompositeIds; + jct.reverseEngineeringStrategyClass = this.reverseEngineeringStrategyClass; + jct.packageName = this.packageName; + jct.revengFiles = this.revengFiles; + jct.detectOneToOne = this.detectOneToOne; + jct.detectManyToMany = this.detectManyToMany; + jct.detectOptimisticLock = this.detectOptimisticLock; + return jct; + } +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JPAConfigurationTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JPAConfigurationTask.java new file mode 100644 index 000000000000..3fa0f3049ac3 --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JPAConfigurationTask.java @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.apache.tools.ant.BuildException; +import org.hibernate.HibernateException; +import org.hibernate.tool.reveng.api.metadata.MetadataDescriptor; +import org.hibernate.tool.reveng.api.metadata.MetadataDescriptorFactory; + +import java.io.File; +import java.util.Properties; + +public class JPAConfigurationTask extends ConfigurationTask { + + String persistenceUnit = null; + + public JPAConfigurationTask() { + setDescription("JPA Configuration"); + } + + protected MetadataDescriptor createMetadataDescriptor() { + try { + Properties overrides = new Properties(); + Properties p = loadPropertiesFile(); + if(p!=null) { + overrides.putAll( p ); + } + return MetadataDescriptorFactory + .createJpaDescriptor(persistenceUnit, overrides); + } + catch(HibernateException t) { + Throwable cause = t.getCause(); + if (cause != null) { + throw new BuildException(cause); + } else { + throw new BuildException("Problems in creating a configuration for JPA. Have you remembered to add hibernate EntityManager jars to the classpath ?",t); + } + } + + } + + public String getPersistenceUnit() { + return persistenceUnit; + } + + public void setPersistenceUnit(String persistenceUnit) { + this.persistenceUnit = persistenceUnit; + } + + public void setConfigurationFile(File configurationFile) { + complain(); + } + + private void complain() { + throw new BuildException("<" + getTaskName() + "> currently only support autodiscovery from META-INF/persistence.xml. Thus setting the configurationfile attribute is not allowed"); + } + + public Object clone() throws CloneNotSupportedException { + JPAConfigurationTask jct = (JPAConfigurationTask) super.clone(); + jct.persistenceUnit = this.persistenceUnit; + return jct; + } + + +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JavaFormatterTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JavaFormatterTask.java new file mode 100644 index 000000000000..7f94021c37b9 --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/JavaFormatterTask.java @@ -0,0 +1,100 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.FileSet; +import org.hibernate.tool.reveng.api.java.DefaultJavaPrettyPrinterStrategy; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; + +public class JavaFormatterTask extends Task { + + private final List fileSets = new ArrayList(); + private boolean failOnError; + + public void addConfiguredFileSet(FileSet fileSet) { + fileSets.add(fileSet); + } + + + private Properties readConfig(File cfgfile) throws IOException { + try (BufferedInputStream stream = new BufferedInputStream( new FileInputStream( cfgfile ) )) { + final Properties settings = new Properties(); + settings.load( stream ); + return settings; + } + } + + + public void execute() throws BuildException { + + File[] files = getFiles(); + + int failed = 0; + + if(files.length>0) { + + DefaultJavaPrettyPrinterStrategy formatter = new DefaultJavaPrettyPrinterStrategy(); + for ( File file : files ) { + try { + boolean ok = formatter.formatFile( file ); + if ( !ok ) { + failed++; + getProject().log( this, "Formatting failed - skipping " + file, Project.MSG_WARN ); + } + else { + getProject().log( this, "Formatted " + file, Project.MSG_VERBOSE ); + } + } + catch (RuntimeException ee) { + failed++; + if ( failOnError ) { + throw new BuildException( "Java formatting failed on " + file, ee ); + } + else { + getProject().log( this, "Java formatting failed on " + file + ", " + ee.getLocalizedMessage(), + Project.MSG_ERR ); + } + } + } + } + + getProject().log( this, "Java formatting of " + files.length + " files completed. Skipped " + failed + " file(s).", Project.MSG_INFO ); + + } + + private File[] getFiles() { + + List files = new LinkedList(); + for ( FileSet fs : fileSets ) { + + DirectoryScanner ds = fs.getDirectoryScanner( getProject() ); + + String[] dsFiles = ds.getIncludedFiles(); + for ( String dsFile : dsFiles ) { + File f = new File( dsFile ); + if ( !f.isFile() ) { + f = new File( ds.getBasedir(), dsFile ); + } + + files.add( f ); + } + } + + return files.toArray( new File[0] ); + } + +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/QueryExporterTask.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/QueryExporterTask.java new file mode 100644 index 000000000000..558dc5a1bfda --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/QueryExporterTask.java @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant; + +import org.apache.tools.ant.BuildException; +import org.hibernate.tool.reveng.api.export.Exporter; +import org.hibernate.tool.reveng.api.export.ExporterConstants; +import org.hibernate.tool.reveng.api.export.ExporterFactory; +import org.hibernate.tool.reveng.api.export.ExporterType; +import org.hibernate.tool.reveng.internal.util.StringUtil; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class QueryExporterTask extends ExporterTask { + + private String query = ""; + private String filename; + List queries = new ArrayList(); + + public QueryExporterTask(HibernateToolTask parent) { + super( parent ); + } + + protected Exporter configureExporter(Exporter exp) { + Exporter exporter = super.configureExporter( exp ); + List queryStrings = new ArrayList(); + if(!StringUtil.isEmptyOrNull(query)) { + queryStrings.add(query); + } + for (Iterator iter = queries.iterator(); iter.hasNext();) { + HQL hql = iter.next(); + if(!StringUtil.isEmptyOrNull(hql.query)) { + queryStrings.add(hql.query); + } + } + exporter.getProperties().put(ExporterConstants.QUERY_LIST, queryStrings); + exporter.getProperties().put(ExporterConstants.OUTPUT_FILE_NAME, filename); + return exporter; + } + + public void validateParameters() { + super.validateParameters(); + if(StringUtil.isEmptyOrNull(query) && queries.isEmpty()) { + throw new BuildException("Need to specify at least one query."); + } + + for (Iterator iter = queries.iterator(); iter.hasNext();) { + HQL hql = iter.next(); + if(StringUtil.isEmptyOrNull(hql.query)) { + throw new BuildException("Query must not be empty"); + } + } + } + protected Exporter createExporter() { + return ExporterFactory.createExporter(ExporterType.QUERY); + } + + public void addText(String text) { + if(!StringUtil.isEmptyOrNull(text)) { + query += trim(text); + } + } + + static private String trim(String text) { + return text.trim(); + } + + public static class HQL { + String query = ""; + public void addText(String text) { + if(!StringUtil.isEmptyOrNull(text)) { + query += trim(text); + } + } + } + + public HQL createHql() { + HQL hql = new HQL(); + queries.add(hql); + return hql; + } + + public void setDestFile(String filename) { + this.filename = filename; + } + + public void execute() { + parent.log("Executing: [" + query + "]"); + super.execute(); + } + public String getName() { + return "query (Executes queries)"; + } + + + boolean isNotEmpty(String string) { + return string != null && string.length() > 0; + } + +} diff --git a/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/util/ExceptionUtil.java b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/util/ExceptionUtil.java new file mode 100644 index 000000000000..928756c07b91 --- /dev/null +++ b/tooling/hibernate-ant/src/main/java/org/hibernate/tool/ant/util/ExceptionUtil.java @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.ant.util; + +import org.hibernate.boot.MappingNotFoundException; +import org.hibernate.boot.jaxb.Origin; + +public class ExceptionUtil { + + public static String getProblemSolutionOrCause(Throwable re) { + if(re==null) return null; + + if(re instanceof MappingNotFoundException) { + MappingNotFoundException mnf = (MappingNotFoundException)re; + Origin origin = mnf.getOrigin(); + return "A " + origin.getType() + " located at " + origin.getName() + " was not found.\n" + + "Check the following:\n" + + "\n" + + "1) Is the spelling/casing correct ?\n" + + "2) Is " + mnf.getOrigin().getName() + " available via the classpath ?\n" + + "3) Does it actually exist ?\n"; + } + + if(re instanceof ClassNotFoundException || re instanceof NoClassDefFoundError) { + + return "A class were not found in the classpath of the Ant task.\n" + + "Ensure that the classpath contains the classes needed for Hibernate and your code are in the classpath.\n"; + + } + + if(re instanceof UnsupportedClassVersionError) { + return "You are most likely running the ant task with a JRE that is older than the JRE required to use the classes.\n" + + "e.g. running with JRE 1.3 or 1.4 when using JDK 1.5 annotations is not possible.\n" + + "Ensure that you are using a correct JRE."; + } + + + + if(re.getCause()!=re) { + return getProblemSolutionOrCause( re.getCause() ); + } + + return null; + } + +} diff --git a/tooling/hibernate-reveng/hibernate-reveng.gradle b/tooling/hibernate-reveng/hibernate-reveng.gradle index ef9129c2509d..332cca181cac 100644 --- a/tooling/hibernate-reveng/hibernate-reveng.gradle +++ b/tooling/hibernate-reveng/hibernate-reveng.gradle @@ -6,7 +6,7 @@ description = "Library providing functionality to perform reverse engineering Hi dependencies { implementation project( ':hibernate-core' ) - implementation project( ':hibernate-ant' ) +// implementation project( ':hibernate-ant' ) implementation "org.apache.commons:commons-collections4:4.5.0" implementation "com.google.googlejavaformat:google-java-format:1.27.0" implementation "org.freemarker:freemarker:2.3.34" diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java new file mode 100644 index 000000000000..fbed8d3dcf4f --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/SchemaExport.java @@ -0,0 +1,638 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.hbm2ddl; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataBuilder; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.model.naming.ImplicitNamingStrategy; +import org.hibernate.boot.model.naming.PhysicalNamingStrategy; +import org.hibernate.boot.registry.BootstrapServiceRegistry; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; +import org.hibernate.boot.registry.selector.spi.StrategySelector; +import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.config.spi.ConfigurationService; +import org.hibernate.internal.log.DeprecationLogger; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.tool.schema.SourceType; +import org.hibernate.tool.schema.TargetType; +import org.hibernate.tool.schema.internal.ExceptionHandlerCollectingImpl; +import org.hibernate.tool.schema.internal.ExceptionHandlerHaltImpl; +import org.hibernate.tool.schema.internal.Helper; +import org.hibernate.tool.schema.internal.SchemaCreatorImpl; +import org.hibernate.tool.schema.spi.ContributableMatcher; +import org.hibernate.tool.schema.spi.ExceptionHandler; +import org.hibernate.tool.schema.spi.ExecutionOptions; +import org.hibernate.tool.schema.spi.SchemaManagementException; +import org.hibernate.tool.schema.spi.SchemaManagementTool; +import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator; +import org.hibernate.tool.schema.spi.ScriptSourceInput; +import org.hibernate.tool.schema.spi.ScriptTargetOutput; +import org.hibernate.tool.schema.spi.SourceDescriptor; +import org.hibernate.tool.schema.spi.TargetDescriptor; + +import java.io.File; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import static org.hibernate.internal.CoreMessageLogger.CORE_LOGGER; + +/** + * Command-line tool for exporting (create and/or drop) a database schema. The export can + * be sent directly to the database, written to script or both. + * + * @author Daniel Bradby + * @author Gavin King + * @author Steve Ebersole + */ +public class SchemaExport { + + public enum Type { + CREATE( Action.CREATE ), + DROP( Action.DROP ), + NONE( Action.NONE ), + BOTH( Action.BOTH ); + + private final Action actionReplacement; + + Type(Action actionReplacement) { + this.actionReplacement = actionReplacement; + } + + public boolean doCreate() { + return actionReplacement.doCreate(); + } + + public boolean doDrop() { + return actionReplacement.doDrop(); + } + } + + public enum Action { + /** + * None - duh :P + */ + NONE, + /** + * Create only + */ + CREATE, + /** + * Drop only + */ + DROP, + /** + * Drop and then create + */ + BOTH; + + public boolean doCreate() { + return this == BOTH || this == CREATE; + } + + public boolean doDrop() { + return this == BOTH || this == DROP; + } + + private static Action interpret(boolean justDrop, boolean justCreate) { + if ( justDrop ) { + return Action.DROP; + } + else if ( justCreate ) { + return Action.CREATE; + } + else { + return Action.BOTH; + } + } + + public static Action parseCommandLineOption(String actionText) { + if ( actionText.equalsIgnoreCase( "create" ) ) { + return CREATE; + } + else if ( actionText.equalsIgnoreCase( "drop" ) ) { + return DROP; + } + else if ( actionText.equalsIgnoreCase( "drop-and-create" ) ) { + return BOTH; + } + else { + return NONE; + } + } + } + + boolean append = true; + boolean haltOnError = false; + boolean format = false; + boolean manageNamespaces = false; + String delimiter = null; + + String outputFile = null; + + private String importFiles; + + private final List exceptions = new ArrayList<>(); + + + /** + * For generating a export script file, this is the file which will be written. + * + * @param filename The name of the file to which to write the export script. + * + * @return this + */ + public SchemaExport setOutputFile(String filename) { + outputFile = filename; + return this; + } + + /** + * For generating a export script file, by default the content will be appended at the begin or end of the file. + * + * The sql will be written at the beginning of the file rather append to the end. + * + * @return this + */ + public SchemaExport setOverrideOutputFileContent() { + append = false; + return this; + } + + /** + * Comma-separated list of resource names to use for database init commands on create. + * + * @param importFiles The comma-separated list of init file resources names + * + * @return this + */ + public SchemaExport setImportFiles(String importFiles) { + this.importFiles = importFiles; + return this; + } + + /** + * Set the end of statement delimiter + * + * @param delimiter The delimiter + * + * @return this + */ + public SchemaExport setDelimiter(String delimiter) { + this.delimiter = delimiter; + return this; + } + + /** + * Should we format the sql strings? + * + * @param format Should we format SQL strings + * + * @return this + */ + public SchemaExport setFormat(boolean format) { + this.format = format; + return this; + } + + /** + * Should we stop once an error occurs? + * + * @param haltOnError True if export should stop after error. + * + * @return this + */ + public SchemaExport setHaltOnError(boolean haltOnError) { + this.haltOnError = haltOnError; + return this; + } + + public SchemaExport setManageNamespaces(boolean manageNamespaces) { + this.manageNamespaces = manageNamespaces; + return this; + } + + public void drop(EnumSet targetTypes, Metadata metadata) { + execute( targetTypes, Action.DROP, metadata ); + } + + public void create(EnumSet targetTypes, Metadata metadata) { + execute( targetTypes, Action.BOTH, metadata ); + } + + public void createOnly(EnumSet targetTypes, Metadata metadata) { + execute( targetTypes, Action.CREATE, metadata ); + } + + public void execute(EnumSet targetTypes, Action action, Metadata metadata) { + execute( targetTypes, action, metadata, ( (MetadataImplementor) metadata ).getMetadataBuildingOptions().getServiceRegistry() ); + } + + public void execute(EnumSet targetTypes, Action action, Metadata metadata, ServiceRegistry serviceRegistry) { + if ( action == Action.NONE ) { + CORE_LOGGER.debug( "Skipping SchemaExport as Action.NONE was passed" ); + return; + } + + if ( targetTypes.isEmpty() ) { + CORE_LOGGER.debug( "Skipping SchemaExport as no targets were specified" ); + return; + } + + exceptions.clear(); + + CORE_LOGGER.runningHbm2ddlSchemaExport(); + + final TargetDescriptor targetDescriptor = buildTargetDescriptor( + targetTypes, + outputFile, + append, + serviceRegistry + ); + + doExecution( action, needsJdbcConnection( targetTypes ), metadata, serviceRegistry, targetDescriptor ); + } + + public void doExecution( + Action action, + boolean needsJdbc, + Metadata metadata, + ServiceRegistry serviceRegistry, + TargetDescriptor targetDescriptor) { + Map config = + new HashMap<>( serviceRegistry.requireService( ConfigurationService.class ).getSettings() ); + config.put( AvailableSettings.HBM2DDL_DELIMITER, delimiter ); + config.put( AvailableSettings.FORMAT_SQL, format ); + config.put( AvailableSettings.HBM2DDL_IMPORT_FILES, importFiles ); + + final SchemaManagementTool tool = serviceRegistry.requireService( SchemaManagementTool.class ); + + final ExceptionHandler exceptionHandler = haltOnError + ? ExceptionHandlerHaltImpl.INSTANCE + : new ExceptionHandlerCollectingImpl(); + final ExecutionOptions executionOptions = SchemaManagementToolCoordinator.buildExecutionOptions( + config, + exceptionHandler + ); + + final SourceDescriptor sourceDescriptor = new SourceDescriptor() { + @Override + public SourceType getSourceType() { + return SourceType.METADATA; + } + + @Override + public ScriptSourceInput getScriptSourceInput() { + return null; + } + }; + + try { + if ( action.doDrop() ) { + tool.getSchemaDropper( config ).doDrop( + metadata, + executionOptions, + ContributableMatcher.ALL, + sourceDescriptor, + targetDescriptor + ); + } + + if ( action.doCreate() ) { + tool.getSchemaCreator( config ).doCreation( + metadata, + executionOptions, + ContributableMatcher.ALL, + sourceDescriptor, + targetDescriptor + ); + } + } + finally { + if ( exceptionHandler instanceof ExceptionHandlerCollectingImpl handler ) { + exceptions.addAll( handler.getExceptions() ); + } + } + } + + private boolean needsJdbcConnection(EnumSet targetTypes) { + return targetTypes.contains( TargetType.DATABASE ); + } + + public static TargetDescriptor buildTargetDescriptor( + EnumSet targetTypes, + String outputFile, + ServiceRegistry serviceRegistry) { + return buildTargetDescriptor( targetTypes, outputFile, true, serviceRegistry ); + } + + public static TargetDescriptor buildTargetDescriptor( + EnumSet targetTypes, + String outputFile, + boolean append, + ServiceRegistry serviceRegistry) { + final ScriptTargetOutput scriptTarget; + if ( targetTypes.contains( TargetType.SCRIPT ) ) { + if ( outputFile == null ) { + throw new SchemaManagementException( "Writing to script was requested, but no script file was specified" ); + } + scriptTarget = Helper.interpretScriptTargetSetting( + outputFile, + serviceRegistry.getService( ClassLoaderService.class ), + (String) serviceRegistry.requireService( ConfigurationService.class ) + .getSettings().get( AvailableSettings.HBM2DDL_CHARSET_NAME ), + append + ); + } + else { + scriptTarget = null; + } + + return new TargetDescriptorImpl( targetTypes, scriptTarget ); + } + + /** + * For testing use + */ + public void perform(Action action, Metadata metadata, ScriptTargetOutput target) { + doExecution( + action, + false, + metadata, + ( (MetadataImplementor) metadata ).getMetadataBuildingOptions().getServiceRegistry(), + new TargetDescriptorImpl( EnumSet.of( TargetType.SCRIPT ), target ) + ); + } + + public static void main(String[] args) { + try { + final CommandLineArgs commandLineArgs = CommandLineArgs.parseCommandLineArgs( args ); + execute( commandLineArgs ); + } + catch (Exception e) { + CORE_LOGGER.unableToCreateSchema( e ); + } + } + + public static void execute(CommandLineArgs commandLineArgs) throws Exception { + StandardServiceRegistry serviceRegistry = buildStandardServiceRegistry( commandLineArgs ); + try { + final MetadataImplementor metadata = buildMetadata( commandLineArgs, serviceRegistry ); + metadata.orderColumns( false ); + metadata.validate(); + + new SchemaExport() + .setHaltOnError( commandLineArgs.halt ) + .setOutputFile( commandLineArgs.outputFile ) + .setDelimiter( commandLineArgs.delimiter ) + .setFormat( commandLineArgs.format ) + .setManageNamespaces( commandLineArgs.manageNamespaces ) + .setImportFiles( commandLineArgs.importFile ) + .execute( commandLineArgs.targetTypes, commandLineArgs.action, metadata, serviceRegistry ); + } + finally { + StandardServiceRegistryBuilder.destroy( serviceRegistry ); + } + } + + private static StandardServiceRegistry buildStandardServiceRegistry(CommandLineArgs commandLineArgs) + throws Exception { + final BootstrapServiceRegistry bsr = new BootstrapServiceRegistryBuilder().build(); + final StandardServiceRegistryBuilder ssrBuilder = new StandardServiceRegistryBuilder( bsr ); + + if ( commandLineArgs.cfgXmlFile != null ) { + ssrBuilder.configure( commandLineArgs.cfgXmlFile ); + } + + Properties properties = new Properties(); + if ( commandLineArgs.propertiesFile != null ) { + try ( final FileInputStream fis = new FileInputStream( commandLineArgs.propertiesFile ) ) { + properties.load( fis ); + } + } + ssrBuilder.applySettings( properties ); + + return ssrBuilder.build(); + } + + private static MetadataImplementor buildMetadata( + CommandLineArgs parsedArgs, + StandardServiceRegistry serviceRegistry) { + final MetadataSources metadataSources = new MetadataSources( serviceRegistry ); + + for ( String filename : parsedArgs.hbmXmlFiles ) { + metadataSources.addFile( filename ); + } + + for ( String filename : parsedArgs.jarFiles ) { + metadataSources.addJar( new File( filename ) ); + } + + + final MetadataBuilder metadataBuilder = metadataSources.getMetadataBuilder(); + final StrategySelector strategySelector = serviceRegistry.requireService( StrategySelector.class ); + if ( parsedArgs.implicitNamingStrategyImplName != null ) { + metadataBuilder.applyImplicitNamingStrategy( + strategySelector.resolveStrategy( + ImplicitNamingStrategy.class, + parsedArgs.implicitNamingStrategyImplName + ) + ); + } + if ( parsedArgs.physicalNamingStrategyImplName != null ) { + metadataBuilder.applyPhysicalNamingStrategy( + strategySelector.resolveStrategy( + PhysicalNamingStrategy.class, + parsedArgs.physicalNamingStrategyImplName + ) + ); + } + + return (MetadataImplementor) metadataBuilder.build(); + } + + /** + * Intended for test usage only. Builds a Metadata using the same algorithm as + * {@link #main} + * + * @param args The "command line args" + * + * @return The built Metadata + * + * @throws Exception Problems building the Metadata + */ + public static MetadataImplementor buildMetadataFromMainArgs(String[] args) throws Exception { + final CommandLineArgs commandLineArgs = CommandLineArgs.parseCommandLineArgs( args ); + StandardServiceRegistry serviceRegistry = buildStandardServiceRegistry( commandLineArgs ); + try { + return buildMetadata( commandLineArgs, serviceRegistry ); + } + finally { + StandardServiceRegistryBuilder.destroy( serviceRegistry ); + } + } + + /** + * Returns a List of all Exceptions which occurred during the export. + * + * @return A List containing the Exceptions occurred during the export + */ + public List getExceptions() { + return exceptions; + } + + private static class CommandLineArgs { + EnumSet targetTypes; + Action action; + + boolean halt = false; + boolean format = false; + + boolean manageNamespaces = false; + + String delimiter = null; + + String outputFile = null; + String importFile = SchemaCreatorImpl.DEFAULT_IMPORT_FILE; + + String propertiesFile = null; + String cfgXmlFile = null; + String implicitNamingStrategyImplName = null; + String physicalNamingStrategyImplName = null; + + List hbmXmlFiles = new ArrayList<>(); + List jarFiles = new ArrayList<>(); + + public static CommandLineArgs parseCommandLineArgs(String[] args) { + String targetText = null; + boolean script = true; + boolean export = true; + + String actionText = null; + boolean drop = false; + boolean create = false; + + CommandLineArgs parsedArgs = new CommandLineArgs(); + + for ( String arg : args ) { + if ( arg.startsWith( "--" ) ) { + if ( arg.equals( "--quiet" ) ) { + script = false; + } + else if ( arg.equals( "--text" ) ) { + export = false; + } + else if ( arg.equals( "--drop" ) ) { + drop = true; + } + else if ( arg.equals( "--create" ) ) { + create = true; + } + else if ( arg.startsWith( "--action=" ) ) { + actionText = arg.substring( 9 ); + } + else if ( arg.startsWith( "--target=" ) ) { + targetText = arg.substring( 9 ); + } + else if ( arg.equals( "--schemas" ) ) { + parsedArgs.manageNamespaces = true; + } + else if ( arg.equals( "--haltonerror" ) ) { + parsedArgs.halt = true; + } + else if ( arg.startsWith( "--output=" ) ) { + parsedArgs.outputFile = arg.substring( 9 ); + } + else if ( arg.startsWith( "--import=" ) ) { + parsedArgs.importFile = arg.substring( 9 ); + } + else if ( arg.startsWith( "--properties=" ) ) { + parsedArgs.propertiesFile = arg.substring( 13 ); + } + else if ( arg.equals( "--format" ) ) { + parsedArgs.format = true; + } + else if ( arg.startsWith( "--delimiter=" ) ) { + parsedArgs.delimiter = arg.substring( 12 ); + } + else if ( arg.startsWith( "--config=" ) ) { + parsedArgs.cfgXmlFile = arg.substring( 9 ); + } + else if ( arg.startsWith( "--naming=" ) ) { + DeprecationLogger.DEPRECATION_LOGGER.logDeprecatedNamingStrategyArgument(); + } + else if ( arg.startsWith( "--implicit-naming=" ) ) { + parsedArgs.implicitNamingStrategyImplName = arg.substring( 18 ); + } + else if ( arg.startsWith( "--physical-naming=" ) ) { + parsedArgs.physicalNamingStrategyImplName = arg.substring( 18 ); + } + } + else { + if ( arg.endsWith( ".jar" ) ) { + parsedArgs.jarFiles.add( arg ); + } + else { + parsedArgs.hbmXmlFiles.add( arg ); + } + } + } + + if ( actionText == null ) { + parsedArgs.action = Action.interpret( drop, create ); + } + else { + if ( drop || create ) { + CORE_LOGGER.warn( "--drop or --create was used; prefer --action=none|create|drop|drop-and-create instead" ); + } + parsedArgs.action = Action.parseCommandLineOption( actionText ); + } + + if ( targetText == null ) { + parsedArgs.targetTypes = TargetTypeHelper.parseLegacyCommandLineOptions( script, export, parsedArgs.outputFile ); + } + else { + if ( !script || !export ) { + CORE_LOGGER.warn( "--text or --quiet was used; prefer --target=none|(stdout|database|script)*" ); + } + parsedArgs.targetTypes = TargetTypeHelper.parseCommandLineOptions( targetText ); + } + + return parsedArgs; + } + } + + private static class TargetDescriptorImpl implements TargetDescriptor { + private final EnumSet targetTypes; + private final ScriptTargetOutput scriptTarget; + + public TargetDescriptorImpl( + EnumSet targetTypes, + ScriptTargetOutput scriptTarget) { + + this.targetTypes = targetTypes; + this.scriptTarget = scriptTarget; + } + + @Override + public EnumSet getTargetTypes() { + return targetTypes; + } + + @Override + public ScriptTargetOutput getScriptTargetOutput() { + return scriptTarget; + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdate.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdate.java new file mode 100644 index 000000000000..8a1792bff0c0 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/SchemaUpdate.java @@ -0,0 +1,321 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.hbm2ddl; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataBuilder; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.model.naming.ImplicitNamingStrategy; +import org.hibernate.boot.model.naming.PhysicalNamingStrategy; +import org.hibernate.boot.registry.BootstrapServiceRegistry; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.registry.selector.spi.StrategySelector; +import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.config.spi.ConfigurationService; +import org.hibernate.internal.log.DeprecationLogger; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.tool.schema.TargetType; +import org.hibernate.tool.schema.internal.ExceptionHandlerCollectingImpl; +import org.hibernate.tool.schema.internal.ExceptionHandlerHaltImpl; +import org.hibernate.tool.schema.spi.ContributableMatcher; +import org.hibernate.tool.schema.spi.ExceptionHandler; +import org.hibernate.tool.schema.spi.ExecutionOptions; +import org.hibernate.tool.schema.spi.SchemaManagementTool; +import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator; +import org.hibernate.tool.schema.spi.TargetDescriptor; + +import java.io.File; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import static org.hibernate.internal.CoreMessageLogger.CORE_LOGGER; + +/** + * A commandline tool to update a database schema. May also be called from inside an application. + * + * @author Christoph Sturm + * @author Steve Ebersole + */ +public class SchemaUpdate { + + private final List exceptions = new ArrayList<>(); + + boolean haltOnError = false; + + private String outputFile; + private boolean append = true; + private String delimiter; + private boolean format; + + public void execute(EnumSet targetTypes, Metadata metadata) { + execute( targetTypes, metadata, ( (MetadataImplementor) metadata ).getMetadataBuildingOptions().getServiceRegistry() ); + } + + public void execute(EnumSet targetTypes, Metadata metadata, ServiceRegistry serviceRegistry) { + if ( targetTypes.isEmpty() ) { + CORE_LOGGER.debug( "Skipping SchemaExport as no targets were specified" ); + return; + } + + exceptions.clear(); + CORE_LOGGER.runningHbm2ddlSchemaUpdate(); + + Map config = + new HashMap<>( serviceRegistry.requireService( ConfigurationService.class ).getSettings() ); + config.put( AvailableSettings.HBM2DDL_DELIMITER, delimiter ); + config.put( AvailableSettings.FORMAT_SQL, format ); + + final SchemaManagementTool tool = serviceRegistry.requireService( SchemaManagementTool.class ); + + final ExceptionHandler exceptionHandler = haltOnError + ? ExceptionHandlerHaltImpl.INSTANCE + : new ExceptionHandlerCollectingImpl(); + + final ExecutionOptions executionOptions = SchemaManagementToolCoordinator.buildExecutionOptions( + config, + exceptionHandler + ); + + final TargetDescriptor targetDescriptor = SchemaExport.buildTargetDescriptor( targetTypes, outputFile, append, serviceRegistry ); + + try { + tool.getSchemaMigrator( config ).doMigration( metadata, executionOptions, ContributableMatcher.ALL, targetDescriptor ); + } + finally { + if ( exceptionHandler instanceof ExceptionHandlerCollectingImpl handler ) { + exceptions.addAll( handler.getExceptions() ); + } + } + } + + /** + * Returns a List of all Exceptions which occurred during the export. + * + * @return A List containing the Exceptions occurred during the export + */ + public List getExceptions() { + return exceptions; + } + + public SchemaUpdate setHaltOnError(boolean haltOnError) { + this.haltOnError = haltOnError; + return this; + } + + public SchemaUpdate setFormat(boolean format) { + this.format = format; + return this; + } + + public SchemaUpdate setOutputFile(String outputFile) { + this.outputFile = outputFile; + return this; + } + + /** + * For generating a export script file, by default the content will be appended at the begin or end of the file. + * + * The sql will be written at the beginning of the file rather append to the end. + * + * @return this + */ + public SchemaUpdate setOverrideOutputFileContent() { + append = false; + return this; + } + + /** + * Set the end of statement delimiter + * + * @param delimiter The delimiter + * + */ + public SchemaUpdate setDelimiter(String delimiter) { + this.delimiter = delimiter; + return this; + } + + public static void main(String[] args) { + try { + final CommandLineArgs parsedArgs = CommandLineArgs.parseCommandLineArgs( args ); + final StandardServiceRegistry serviceRegistry = buildStandardServiceRegistry( parsedArgs ); + + try { + final MetadataImplementor metadata = buildMetadata( parsedArgs, serviceRegistry ); + + new SchemaUpdate() + .setOutputFile( parsedArgs.outputFile ) + .setDelimiter( parsedArgs.delimiter ) + .execute( parsedArgs.targetTypes, metadata, serviceRegistry ); + } + finally { + StandardServiceRegistryBuilder.destroy( serviceRegistry ); + } + } + catch (Exception e) { + CORE_LOGGER.unableToRunSchemaUpdate( e ); + } + } + + private static StandardServiceRegistry buildStandardServiceRegistry(CommandLineArgs parsedArgs) throws Exception { + final BootstrapServiceRegistry bsr = new BootstrapServiceRegistryBuilder().build(); + final StandardServiceRegistryBuilder ssrBuilder = new StandardServiceRegistryBuilder( bsr ); + + if ( parsedArgs.cfgXmlFile != null ) { + ssrBuilder.configure( parsedArgs.cfgXmlFile ); + } + + if ( parsedArgs.propertiesFile != null ) { + Properties props = new Properties(); + try ( final FileInputStream fis = new FileInputStream( parsedArgs.propertiesFile ) ) { + props.load( fis ); + } + ssrBuilder.applySettings( props ); + } + + return ssrBuilder.build(); + } + + private static MetadataImplementor buildMetadata(CommandLineArgs parsedArgs, ServiceRegistry serviceRegistry) { + final MetadataSources metadataSources = new MetadataSources( serviceRegistry ); + + for ( String filename : parsedArgs.hbmXmlFiles ) { + metadataSources.addFile( filename ); + } + + for ( String filename : parsedArgs.jarFiles ) { + metadataSources.addJar( new File( filename ) ); + } + + + final MetadataBuilder metadataBuilder = metadataSources.getMetadataBuilder(); + final StrategySelector strategySelector = serviceRegistry.requireService( StrategySelector.class ); + if ( parsedArgs.implicitNamingStrategyImplName != null ) { + metadataBuilder.applyImplicitNamingStrategy( + strategySelector.resolveStrategy( + ImplicitNamingStrategy.class, + parsedArgs.implicitNamingStrategyImplName + ) + ); + } + if ( parsedArgs.physicalNamingStrategyImplName != null ) { + metadataBuilder.applyPhysicalNamingStrategy( + strategySelector.resolveStrategy( + PhysicalNamingStrategy.class, + parsedArgs.physicalNamingStrategyImplName + ) + ); + } + + return (MetadataImplementor) metadataBuilder.build(); + } + + private static class CommandLineArgs { + EnumSet targetTypes; + + String propertiesFile = null; + String cfgXmlFile = null; + String outputFile = null; + String delimiter = null; + + String implicitNamingStrategyImplName = null; + String physicalNamingStrategyImplName = null; + + List hbmXmlFiles = new ArrayList<>(); + List jarFiles = new ArrayList<>(); + + public static CommandLineArgs parseCommandLineArgs(String[] args) { + final CommandLineArgs parsedArgs = new CommandLineArgs(); + + String targetText = null; + boolean script = true; + boolean doUpdate = true; + + for ( String arg : args ) { + if ( arg.startsWith( "--" ) ) { + if ( arg.equals( "--quiet" ) ) { + script = false; + } + else if ( arg.startsWith( "--text" ) ) { + doUpdate = false; + } + else if ( arg.startsWith( "--target=" ) ) { + targetText = arg.substring( 9 ); + } + else if ( arg.startsWith( "--properties=" ) ) { + parsedArgs.propertiesFile = arg.substring( 13 ); + } + else if ( arg.startsWith( "--config=" ) ) { + parsedArgs.cfgXmlFile = arg.substring( 9 ); + } + else if ( arg.startsWith( "--output=" ) ) { + parsedArgs.outputFile = arg.substring( 9 ); + } + else if ( arg.startsWith( "--naming=" ) ) { + DeprecationLogger.DEPRECATION_LOGGER.logDeprecatedNamingStrategyArgument(); + } + else if ( arg.startsWith( "--delimiter=" ) ) { + parsedArgs.delimiter = arg.substring( 12 ); + } + else if ( arg.startsWith( "--implicit-naming=" ) ) { + parsedArgs.implicitNamingStrategyImplName = arg.substring( 18 ); + } + else if ( arg.startsWith( "--physical-naming=" ) ) { + parsedArgs.physicalNamingStrategyImplName = arg.substring( 18 ); + } + } + else { + if ( arg.endsWith( ".jar" ) ) { + parsedArgs.jarFiles.add( arg ); + } + else { + parsedArgs.hbmXmlFiles.add( arg ); + } + } + } + + if ( targetText == null ) { + parsedArgs.targetTypes = TargetTypeHelper.parseLegacyCommandLineOptions( script, doUpdate, parsedArgs.outputFile ); + } + else { + if ( !script || !doUpdate ) { + CORE_LOGGER.warn( "--text or --quiet was used; prefer --target=none|(stdout|database|script)*" ); + } + parsedArgs.targetTypes = TargetTypeHelper.parseCommandLineOptions( targetText ); + } + + return parsedArgs; + } + } + + /** + * Intended for test usage only. Builds a Metadata using the same algorithm as + * {@link #main} + * + * @param args The "command line args" + * + * @return The built Metadata + * + * @throws Exception Problems building the Metadata + */ + public static MetadataImplementor buildMetadataFromMainArgs(String[] args) throws Exception { + final CommandLineArgs commandLineArgs = CommandLineArgs.parseCommandLineArgs( args ); + StandardServiceRegistry serviceRegistry = buildStandardServiceRegistry( commandLineArgs ); + try { + return buildMetadata( commandLineArgs, serviceRegistry ); + } + finally { + StandardServiceRegistryBuilder.destroy( serviceRegistry ); + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/Target.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/Target.java new file mode 100644 index 000000000000..819b80cea232 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/Target.java @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.hbm2ddl; + +/** + * Describes the types of targets for create, drop and update actions + * + * @author Steve Ebersole + */ +public enum Target { + /** + * Export to the database. + */ + EXPORT, + /** + * Write to a script file. + */ + SCRIPT, + /** + * export nowhere + */ + NONE, + /** + * Do both {@link #EXPORT} and {@link #SCRIPT} + */ + BOTH; + + public boolean doExport() { + return this == BOTH || this == EXPORT; + } + + public boolean doScript() { + return this == BOTH || this == SCRIPT; + } + + public static Target interpret(boolean script, boolean export) { + if ( script ) { + return export ? BOTH : SCRIPT; + } + else { + return export ? EXPORT : NONE; + } + } +} diff --git a/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/TargetTypeHelper.java b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/TargetTypeHelper.java new file mode 100644 index 000000000000..7c0673c18089 --- /dev/null +++ b/tooling/hibernate-reveng/src/main/java/org/hibernate/tool/hbm2ddl/TargetTypeHelper.java @@ -0,0 +1,53 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.tool.hbm2ddl; + +import org.hibernate.tool.schema.TargetType; + +import java.util.EnumSet; + +/** + * @author Steve Ebersole + */ +public class TargetTypeHelper { + public static EnumSet parseLegacyCommandLineOptions(boolean script, boolean export, String outputFile) { + final EnumSet options = EnumSet.noneOf( TargetType.class ); + + final Target target = Target.interpret( script, export ); + if ( outputFile != null ) { + options.add( TargetType.SCRIPT ); + } + if ( target.doScript() ) { + options.add( TargetType.STDOUT ); + } + if ( target.doExport() ) { + options.add( TargetType.DATABASE ); + } + return options; + } + + public static EnumSet parseCommandLineOptions(String targetTypeText) { + final EnumSet options = EnumSet.noneOf( TargetType.class ); + + if ( !targetTypeText.equalsIgnoreCase( "none" ) ) { + for ( String option : targetTypeText.split( "," ) ) { + if ( option.equalsIgnoreCase( "database" ) ) { + options.add( TargetType.DATABASE ); + } + else if ( option.equalsIgnoreCase( "stdout" ) ) { + options.add( TargetType.STDOUT ); + } + else if ( option.equalsIgnoreCase( "script" ) ) { + options.add( TargetType.SCRIPT ); + } + else { + throw new IllegalArgumentException( "Unrecognized --target option : " + option ); + } + } + } + + return options; + } +}