kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 1 | /******************************************************************************* |
| 2 | * Copyright (c) 2006, 2011 Oracle. All rights reserved. |
| 3 | * This program and the accompanying materials are made available under the |
| 4 | * terms of the Eclipse Public License v1.0, which accompanies this distribution |
| 5 | * and is available at http://www.eclipse.org/legal/epl-v10.html. |
| 6 | * |
| 7 | * Contributors: |
| 8 | * Oracle - initial API and implementation |
| 9 | ******************************************************************************/ |
| 10 | package org.eclipse.jpt.jpa.core.internal.context.orm; |
| 11 | |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 12 | import java.util.List; |
| 13 | import org.eclipse.jdt.core.IPackageFragment; |
| 14 | import org.eclipse.jdt.core.IType; |
| 15 | import org.eclipse.jpt.common.core.utility.TextRange; |
kmoore | bbf1de6 | 2011-08-25 20:29:52 +0000 | [diff] [blame] | 16 | import org.eclipse.jpt.common.utility.internal.StringTools; |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 17 | import org.eclipse.jpt.common.utility.internal.iterables.CompositeIterable; |
| 18 | import org.eclipse.jpt.common.utility.internal.iterables.EmptyIterable; |
| 19 | import org.eclipse.jpt.common.utility.internal.iterables.SingleElementIterable; |
kmoore | 3d505d2 | 2011-08-02 13:24:47 +0000 | [diff] [blame] | 20 | import org.eclipse.jpt.common.utility.internal.iterables.TransformationIterable; |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 21 | import org.eclipse.jpt.jpa.core.context.AttributeMapping; |
| 22 | import org.eclipse.jpt.jpa.core.context.Entity; |
| 23 | import org.eclipse.jpt.jpa.core.context.FetchType; |
| 24 | import org.eclipse.jpt.jpa.core.context.PersistentAttribute; |
| 25 | import org.eclipse.jpt.jpa.core.context.PersistentType; |
| 26 | import org.eclipse.jpt.jpa.core.context.ReadOnlyPersistentAttribute; |
| 27 | import org.eclipse.jpt.jpa.core.context.RelationshipMapping; |
| 28 | import org.eclipse.jpt.jpa.core.context.TypeMapping; |
| 29 | import org.eclipse.jpt.jpa.core.context.orm.OrmCascade; |
| 30 | import org.eclipse.jpt.jpa.core.context.orm.OrmMappingRelationship; |
| 31 | import org.eclipse.jpt.jpa.core.context.orm.OrmPersistentAttribute; |
| 32 | import org.eclipse.jpt.jpa.core.context.orm.OrmRelationshipMapping; |
| 33 | import org.eclipse.jpt.jpa.core.internal.context.AttributeMappingTools; |
| 34 | import org.eclipse.jpt.jpa.core.internal.jpa1.context.orm.GenericOrmCascade; |
| 35 | import org.eclipse.jpt.jpa.core.internal.validation.DefaultJpaValidationMessages; |
| 36 | import org.eclipse.jpt.jpa.core.internal.validation.JpaValidationMessages; |
| 37 | import org.eclipse.jpt.jpa.core.jpa2.context.MetamodelField; |
| 38 | import org.eclipse.jpt.jpa.core.resource.orm.AbstractXmlRelationshipMapping; |
| 39 | import org.eclipse.text.edits.ReplaceEdit; |
| 40 | import org.eclipse.wst.validation.internal.provisional.core.IMessage; |
| 41 | import org.eclipse.wst.validation.internal.provisional.core.IReporter; |
| 42 | |
| 43 | /** |
| 44 | * <code>orm.xml</code> relationship mapping (1:1, 1:m, m:1, m:m) |
| 45 | */ |
| 46 | public abstract class AbstractOrmRelationshipMapping<X extends AbstractXmlRelationshipMapping> |
| 47 | extends AbstractOrmAttributeMapping<X> |
| 48 | implements OrmRelationshipMapping |
| 49 | { |
| 50 | protected String specifiedTargetEntity; |
| 51 | protected String defaultTargetEntity; |
| 52 | |
| 53 | protected final OrmMappingRelationship relationship; |
| 54 | |
| 55 | protected final OrmCascade cascade; |
| 56 | |
| 57 | protected FetchType specifiedFetch; |
| 58 | protected FetchType defaultFetch; |
| 59 | |
| 60 | |
| 61 | protected AbstractOrmRelationshipMapping(OrmPersistentAttribute parent, X xmlMapping) { |
| 62 | super(parent, xmlMapping); |
| 63 | this.specifiedTargetEntity = xmlMapping.getTargetEntity(); |
| 64 | this.relationship = this.buildRelationship(); |
| 65 | this.cascade = this.buildCascade(); |
| 66 | this.specifiedFetch = this.buildSpecifiedFetch(); |
| 67 | } |
| 68 | |
| 69 | |
| 70 | // ********** synchronize/update ********** |
| 71 | |
| 72 | @Override |
| 73 | public void synchronizeWithResourceModel() { |
| 74 | super.synchronizeWithResourceModel(); |
| 75 | this.setSpecifiedTargetEntity_(this.xmlAttributeMapping.getTargetEntity()); |
| 76 | this.relationship.synchronizeWithResourceModel(); |
| 77 | this.cascade.synchronizeWithResourceModel(); |
| 78 | this.setSpecifiedFetch_(this.buildSpecifiedFetch()); |
| 79 | } |
| 80 | |
| 81 | @Override |
| 82 | public void update() { |
| 83 | super.update(); |
| 84 | this.setDefaultTargetEntity(this.buildDefaultTargetEntity()); |
| 85 | this.relationship.update(); |
| 86 | this.cascade.update(); |
| 87 | this.setDefaultFetch(this.buildDefaultFetch()); |
| 88 | } |
| 89 | |
| 90 | |
| 91 | // ********** target entity ********** |
| 92 | |
| 93 | public String getTargetEntity() { |
| 94 | return (this.specifiedTargetEntity != null) ? this.specifiedTargetEntity : this.defaultTargetEntity; |
| 95 | } |
| 96 | |
| 97 | public String getSpecifiedTargetEntity() { |
| 98 | return this.specifiedTargetEntity; |
| 99 | } |
| 100 | |
| 101 | public void setSpecifiedTargetEntity(String entity) { |
| 102 | this.setSpecifiedTargetEntity_(entity); |
| 103 | this.xmlAttributeMapping.setTargetEntity(entity); |
| 104 | } |
| 105 | |
| 106 | protected void setSpecifiedTargetEntity_(String entity) { |
| 107 | String old = this.specifiedTargetEntity; |
| 108 | this.specifiedTargetEntity = entity; |
| 109 | this.firePropertyChanged(SPECIFIED_TARGET_ENTITY_PROPERTY, old, entity); |
| 110 | } |
| 111 | |
| 112 | public String getDefaultTargetEntity() { |
| 113 | return this.defaultTargetEntity; |
| 114 | } |
| 115 | |
| 116 | protected void setDefaultTargetEntity(String entity) { |
| 117 | String old = this.defaultTargetEntity; |
| 118 | this.defaultTargetEntity = entity; |
| 119 | this.firePropertyChanged(DEFAULT_TARGET_ENTITY_PROPERTY, old, entity); |
| 120 | } |
| 121 | |
| 122 | protected String buildDefaultTargetEntity() { |
| 123 | return (this.getJavaPersistentAttribute() == null) ? null : this.getJavaTargetType(); |
| 124 | } |
| 125 | |
| 126 | /** |
| 127 | * pre-condition: the mapping's Java persistent attribute is not |
| 128 | * <code>null</code>. |
| 129 | */ |
| 130 | protected abstract String getJavaTargetType(); |
| 131 | |
| 132 | public Entity getResolvedTargetEntity() { |
| 133 | TypeMapping typeMapping = this.getResolvedTargetTypeMapping(); |
| 134 | return (typeMapping instanceof Entity) ? (Entity) typeMapping : null; |
| 135 | } |
| 136 | |
| 137 | protected TypeMapping getResolvedTargetTypeMapping() { |
| 138 | PersistentType resolvedTargetType = this.getResolvedTargetType(); |
| 139 | return (resolvedTargetType == null) ? null : resolvedTargetType.getMapping(); |
| 140 | } |
| 141 | |
| 142 | // sub-classes like this to be public |
| 143 | public PersistentType getResolvedTargetType() { |
| 144 | return this.resolvePersistentType(this.getTargetEntity()); |
| 145 | } |
| 146 | |
| 147 | public char getTargetEntityEnclosingTypeSeparator() { |
| 148 | return '$'; |
| 149 | } |
| 150 | |
| 151 | |
| 152 | // ********** relationship reference ********** |
| 153 | |
| 154 | public OrmMappingRelationship getRelationship() { |
| 155 | return this.relationship; |
| 156 | } |
| 157 | |
| 158 | protected abstract OrmMappingRelationship buildRelationship(); |
| 159 | |
| 160 | |
| 161 | // ********** cascade ********** |
| 162 | |
| 163 | public OrmCascade getCascade() { |
| 164 | return this.cascade; |
| 165 | } |
| 166 | |
| 167 | protected OrmCascade buildCascade() { |
| 168 | // NB: we don't use the platform |
| 169 | return new GenericOrmCascade(this); |
| 170 | } |
| 171 | |
| 172 | |
| 173 | // ********** fetch ********** |
| 174 | |
| 175 | public FetchType getFetch() { |
| 176 | return (this.specifiedFetch != null) ? this.specifiedFetch : this.defaultFetch; |
| 177 | } |
| 178 | |
| 179 | public FetchType getSpecifiedFetch() { |
| 180 | return this.specifiedFetch; |
| 181 | } |
| 182 | |
| 183 | public void setSpecifiedFetch(FetchType fetch) { |
| 184 | this.setSpecifiedFetch_(fetch); |
| 185 | this.xmlAttributeMapping.setFetch(FetchType.toOrmResourceModel(fetch)); |
| 186 | } |
| 187 | |
| 188 | protected void setSpecifiedFetch_(FetchType fetch) { |
| 189 | FetchType old = this.specifiedFetch; |
| 190 | this.specifiedFetch = fetch; |
| 191 | this.firePropertyChanged(SPECIFIED_FETCH_PROPERTY, old, fetch); |
| 192 | } |
| 193 | |
| 194 | protected FetchType buildSpecifiedFetch() { |
| 195 | return FetchType.fromOrmResourceModel(this.xmlAttributeMapping.getFetch()); |
| 196 | } |
| 197 | |
| 198 | public FetchType getDefaultFetch() { |
| 199 | return this.defaultFetch; |
| 200 | } |
| 201 | |
| 202 | protected void setDefaultFetch(FetchType fetch) { |
| 203 | FetchType old = this.defaultFetch; |
| 204 | this.defaultFetch = fetch; |
| 205 | this.firePropertyChanged(DEFAULT_FETCH_PROPERTY, old, fetch); |
| 206 | } |
| 207 | |
| 208 | protected abstract FetchType buildDefaultFetch(); |
| 209 | |
| 210 | |
| 211 | // ********** misc ********** |
| 212 | |
| 213 | @Override |
| 214 | public boolean isRelationshipOwner() { |
| 215 | return this.relationship.isOwner(); |
| 216 | } |
| 217 | |
| 218 | @Override |
| 219 | public boolean isOwnedBy(AttributeMapping mapping) { |
| 220 | return mapping.isRelationshipOwner() && |
| 221 | this.relationship.isOwnedBy((RelationshipMapping) mapping); |
| 222 | } |
| 223 | |
| 224 | public RelationshipMapping getRelationshipOwner() { |
| 225 | Entity entity = this.getResolvedTargetEntity(); |
| 226 | if (entity == null) { |
| 227 | return null; |
| 228 | } |
kmoore | c7a0867 | 2011-08-01 15:10:18 +0000 | [diff] [blame] | 229 | for (ReadOnlyPersistentAttribute attribute : entity.getPersistentType().getAllAttributes()) { |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 230 | AttributeMapping mapping = attribute.getMapping(); |
| 231 | if (this.isOwnedBy(mapping)) { |
| 232 | return (RelationshipMapping) mapping; |
| 233 | } |
| 234 | } |
| 235 | return null; |
| 236 | } |
| 237 | |
| 238 | @Override |
| 239 | public boolean isOverridableAssociationMapping() { |
| 240 | return this.relationship.isOverridable(); |
| 241 | } |
| 242 | |
| 243 | @Override |
| 244 | protected void initializeFromOrmRelationshipMapping(OrmRelationshipMapping oldMapping) { |
| 245 | super.initializeFromOrmRelationshipMapping(oldMapping); |
| 246 | this.setSpecifiedTargetEntity(oldMapping.getSpecifiedTargetEntity()); |
| 247 | this.setSpecifiedFetch(oldMapping.getSpecifiedFetch()); |
| 248 | oldMapping.getRelationship().initializeOn(this.relationship); |
| 249 | this.cascade.initializeFrom(oldMapping.getCascade()); |
| 250 | //TODO should we set the fetch type from a BasicMapping?? |
| 251 | } |
| 252 | |
kmoore | 3d505d2 | 2011-08-02 13:24:47 +0000 | [diff] [blame] | 253 | public Iterable<String> getAllTargetEntityAttributeNames() { |
| 254 | return new CompositeIterable<String>(this.getAllTargetEntityAttributeNamesLists()); |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 255 | } |
| 256 | |
kmoore | 3d505d2 | 2011-08-02 13:24:47 +0000 | [diff] [blame] | 257 | protected Iterable<Iterable<String>> getAllTargetEntityAttributeNamesLists() { |
| 258 | return new TransformationIterable<AttributeMapping, Iterable<String>>(this.getAllTargetEntityAttributeMappings(), AttributeMappingTools.ALL_MAPPING_NAMES_TRANSFORMER); |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 259 | } |
| 260 | |
kmoore | 3d505d2 | 2011-08-02 13:24:47 +0000 | [diff] [blame] | 261 | protected Iterable<AttributeMapping> getAllTargetEntityAttributeMappings() { |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 262 | Entity entity = this.getResolvedTargetEntity(); |
kmoore | 3d505d2 | 2011-08-02 13:24:47 +0000 | [diff] [blame] | 263 | return (entity != null) ? entity.getAllAttributeMappings() : EmptyIterable.<AttributeMapping>instance(); |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 264 | } |
| 265 | |
| 266 | protected String getTargetEntityIdAttributeName() { |
| 267 | PersistentAttribute attribute = this.getTargetEntityIdAttribute(); |
| 268 | return (attribute == null) ? null : attribute.getName(); |
| 269 | } |
| 270 | |
| 271 | protected PersistentAttribute getTargetEntityIdAttribute() { |
| 272 | Entity entity = this.getResolvedTargetEntity(); |
| 273 | return (entity == null) ? null : entity.getIdAttribute(); |
| 274 | } |
| 275 | |
| 276 | |
| 277 | //************ refactoring ************ |
| 278 | |
| 279 | @Override |
| 280 | @SuppressWarnings("unchecked") |
| 281 | public Iterable<ReplaceEdit> createRenameTypeEdits(IType originalType, String newName) { |
| 282 | return new CompositeIterable<ReplaceEdit>( |
| 283 | super.createRenameTypeEdits(originalType, newName), |
| 284 | this.createTargetEntityRenameTypeEdits(originalType, newName) |
| 285 | ); |
| 286 | } |
| 287 | |
| 288 | protected Iterable<ReplaceEdit> createTargetEntityRenameTypeEdits(IType originalType, String newName) { |
| 289 | if (this.specifiedTargetEntity != null) { |
| 290 | PersistentType targetType = this.getResolvedTargetType(); |
| 291 | if ((targetType != null) && targetType.isFor(originalType.getFullyQualifiedName('.'))) { |
| 292 | return new SingleElementIterable<ReplaceEdit>(this.createTargetEntityRenameTypeEdit(originalType, newName)); |
| 293 | } |
| 294 | } |
| 295 | return EmptyIterable.instance(); |
| 296 | } |
| 297 | |
| 298 | protected ReplaceEdit createTargetEntityRenameTypeEdit(IType originalType, String newName) { |
| 299 | return this.xmlAttributeMapping.createRenameTargetEntityEdit(originalType, newName); |
| 300 | } |
| 301 | |
| 302 | @Override |
| 303 | @SuppressWarnings("unchecked") |
| 304 | public Iterable<ReplaceEdit> createMoveTypeEdits(IType originalType, IPackageFragment newPackage) { |
| 305 | return new CompositeIterable<ReplaceEdit>( |
| 306 | super.createMoveTypeEdits(originalType, newPackage), |
| 307 | this.createTargetEntityMoveTypeEdits(originalType, newPackage) |
| 308 | ); |
| 309 | } |
| 310 | |
| 311 | protected Iterable<ReplaceEdit> createTargetEntityMoveTypeEdits(IType originalType, IPackageFragment newPackage) { |
| 312 | if (this.specifiedTargetEntity != null) { |
| 313 | PersistentType targetType = this.getResolvedTargetType(); |
| 314 | if ((targetType != null) && targetType.isFor(originalType.getFullyQualifiedName('.'))) { |
| 315 | return new SingleElementIterable<ReplaceEdit>(this.createTargetEntityRenamePackageEdit(newPackage.getElementName())); |
| 316 | } |
| 317 | } |
| 318 | return EmptyIterable.instance(); |
| 319 | } |
| 320 | |
| 321 | @Override |
| 322 | @SuppressWarnings("unchecked") |
| 323 | public Iterable<ReplaceEdit> createRenamePackageEdits(IPackageFragment originalPackage, String newName) { |
| 324 | return new CompositeIterable<ReplaceEdit>( |
| 325 | super.createRenamePackageEdits(originalPackage, newName), |
| 326 | this.createTargetEntityRenamePackageEdits(originalPackage, newName) |
| 327 | ); |
| 328 | } |
| 329 | |
| 330 | protected Iterable<ReplaceEdit> createTargetEntityRenamePackageEdits(IPackageFragment originalPackage, String newName) { |
| 331 | if (this.specifiedTargetEntity != null) { |
| 332 | PersistentType targetType = this.getResolvedTargetType(); |
| 333 | if ((targetType != null) && targetType.isIn(originalPackage)) { |
| 334 | return new SingleElementIterable<ReplaceEdit>(this.createTargetEntityRenamePackageEdit(newName)); |
| 335 | } |
| 336 | } |
| 337 | return EmptyIterable.instance(); |
| 338 | } |
| 339 | |
| 340 | protected ReplaceEdit createTargetEntityRenamePackageEdit(String newName) { |
| 341 | return this.xmlAttributeMapping.createRenameTargetEntityPackageEdit(newName); |
| 342 | } |
| 343 | |
| 344 | |
| 345 | // ********** validation ********** |
| 346 | |
| 347 | @Override |
| 348 | public void validate(List<IMessage> messages, IReporter reporter) { |
| 349 | super.validate(messages, reporter); |
| 350 | this.validateTargetEntity(messages); |
| 351 | this.relationship.validate(messages, reporter); |
| 352 | } |
| 353 | |
| 354 | protected void validateTargetEntity(List<IMessage> messages) { |
kmoore | bbf1de6 | 2011-08-25 20:29:52 +0000 | [diff] [blame] | 355 | if (StringTools.stringIsEmpty(this.getTargetEntity())) { |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 356 | messages.add( |
| 357 | DefaultJpaValidationMessages.buildMessage( |
| 358 | IMessage.HIGH_SEVERITY, |
bvosburgh | 91d39dc | 2011-04-28 02:00:43 +0000 | [diff] [blame] | 359 | JpaValidationMessages.TARGET_ENTITY_NOT_DEFINED, |
kmoore | bbf1de6 | 2011-08-25 20:29:52 +0000 | [diff] [blame] | 360 | EMPTY_STRING_ARRAY, |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 361 | this, |
kmoore | bbf1de6 | 2011-08-25 20:29:52 +0000 | [diff] [blame] | 362 | this.getTargetEntityTextRange() |
| 363 | ) |
| 364 | ); |
| 365 | return; |
| 366 | } |
| 367 | if ( ! this.targetEntityExists()) { |
| 368 | messages.add( |
| 369 | DefaultJpaValidationMessages.buildMessage( |
| 370 | IMessage.HIGH_SEVERITY, |
| 371 | JpaValidationMessages.TARGET_ENTITY_NOT_EXIST, |
| 372 | EMPTY_STRING_ARRAY, |
| 373 | this, |
| 374 | this.getTargetEntityTextRange() |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 375 | ) |
| 376 | ); |
bvosburgh | 91d39dc | 2011-04-28 02:00:43 +0000 | [diff] [blame] | 377 | return; |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 378 | } |
bvosburgh | 91d39dc | 2011-04-28 02:00:43 +0000 | [diff] [blame] | 379 | if (this.getResolvedTargetEntity() == null) { |
| 380 | messages.add( |
| 381 | DefaultJpaValidationMessages.buildMessage( |
| 382 | IMessage.HIGH_SEVERITY, |
| 383 | JpaValidationMessages.TARGET_ENTITY_IS_NOT_AN_ENTITY, |
kmoore | bbf1de6 | 2011-08-25 20:29:52 +0000 | [diff] [blame] | 384 | new String[] {this.getTargetEntity()}, |
bvosburgh | 91d39dc | 2011-04-28 02:00:43 +0000 | [diff] [blame] | 385 | this, |
| 386 | this.getTargetEntityTextRange() |
| 387 | ) |
| 388 | ); |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 389 | } |
| 390 | } |
| 391 | |
kmoore | bbf1de6 | 2011-08-25 20:29:52 +0000 | [diff] [blame] | 392 | protected boolean targetEntityExists() { |
| 393 | return getEntityMappings().resolveJdtType(this.getTargetEntity()) != null; |
| 394 | } |
| 395 | |
| 396 | |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 397 | protected TextRange getTargetEntityTextRange() { |
bvosburgh | 91d39dc | 2011-04-28 02:00:43 +0000 | [diff] [blame] | 398 | return this.getValidationTextRange(this.xmlAttributeMapping.getTargetEntityTextRange()); |
kmoore | c9c9e2b | 2011-02-06 02:07:28 +0000 | [diff] [blame] | 399 | } |
| 400 | |
| 401 | |
| 402 | // ********** metamodel ********** |
| 403 | |
| 404 | @Override |
| 405 | public String getMetamodelTypeName() { |
| 406 | PersistentType resolvedTargetType = this.getResolvedTargetType(); |
| 407 | if (resolvedTargetType == null) { |
| 408 | return MetamodelField.DEFAULT_TYPE_NAME; |
| 409 | } |
| 410 | String targetTypeName = resolvedTargetType.getName(); |
| 411 | return (targetTypeName != null) ? targetTypeName : MetamodelField.DEFAULT_TYPE_NAME; |
| 412 | } |
| 413 | |
| 414 | } |