001package systems.dmx.core.impl; 002 003import systems.dmx.core.ChildTopics; 004import systems.dmx.core.RelatedTopic; 005import systems.dmx.core.Topic; 006import systems.dmx.core.model.ChildTopicsModel; 007import systems.dmx.core.model.RelatedTopicModel; 008import systems.dmx.core.model.SimpleValue; 009import systems.dmx.core.model.TopicModel; 010import systems.dmx.core.service.ModelFactory; 011 012import java.util.ArrayList; 013import java.util.Iterator; 014import java.util.List; 015import java.util.logging.Logger; 016 017 018 019/** 020 * A child topics model that is attached to the DB. 021 */ 022class ChildTopicsImpl implements ChildTopics { 023 024 // ---------------------------------------------------------------------------------------------- Instance Variables 025 026 private ChildTopicsModelImpl model; // underlying model 027 028 private DMXObjectModelImpl parent; // the parent object this ChildTopics belongs to 029 030 private PersistenceLayer pl; 031 private ModelFactory mf; 032 033 private Logger logger = Logger.getLogger(getClass().getName()); 034 035 // ---------------------------------------------------------------------------------------------------- Constructors 036 037 ChildTopicsImpl(ChildTopicsModelImpl model, DMXObjectModelImpl parent, PersistenceLayer pl) { 038 this.model = model; 039 this.parent = parent; 040 this.pl = pl; 041 this.mf = pl.mf; 042 } 043 044 // -------------------------------------------------------------------------------------------------- Public Methods 045 046 047 048 // ********************************** 049 // *** ChildTopics Implementation *** 050 // ********************************** 051 052 053 054 // === Accessors === 055 056 @Override 057 public RelatedTopic getTopic(String assocDefUri) { 058 loadChildTopics(assocDefUri); 059 return _getTopic(assocDefUri); 060 } 061 062 @Override 063 public RelatedTopic getTopicOrNull(String assocDefUri) { 064 loadChildTopics(assocDefUri); 065 return _getTopicOrNull(assocDefUri); 066 } 067 068 @Override 069 public List<RelatedTopic> getTopics(String assocDefUri) { 070 loadChildTopics(assocDefUri); 071 return _getTopics(assocDefUri); 072 } 073 074 @Override 075 public List<RelatedTopic> getTopicsOrNull(String assocDefUri) { 076 loadChildTopics(assocDefUri); 077 return _getTopicsOrNull(assocDefUri); 078 } 079 080 // --- 081 082 @Override 083 public Object get(String assocDefUri) { 084 Object value = model.get(assocDefUri); 085 // Note: topics just created have no child topics yet 086 if (value == null) { 087 return null; 088 } 089 // Note: no direct recursion takes place here. Recursion is indirect: attached topics are created here, this 090 // implies creating further ChildTopicsImpl objects, which in turn calls this method again but for the next 091 // child-level. Finally attached topics are created for all child-levels. ### FIXME 092 if (value instanceof RelatedTopicModel) { 093 return instantiate((RelatedTopicModel) value); 094 } else if (value instanceof List) { 095 return instantiate((List<RelatedTopicModel>) value); 096 } else { 097 throw new RuntimeException("Unexpected value in a ChildTopicsModel: " + value); 098 } 099 } 100 101 // --- 102 103 @Override 104 public ChildTopicsModel getModel() { 105 return model; 106 } 107 108 109 110 // === Convenience Accessors === 111 112 @Override 113 public String getString(String assocDefUri) { 114 return getTopic(assocDefUri).getSimpleValue().toString(); 115 } 116 117 @Override 118 public String getStringOrNull(String assocDefUri) { 119 Topic topic = getTopicOrNull(assocDefUri); 120 return topic != null ? topic.getSimpleValue().toString() : null; 121 } 122 123 @Override 124 public int getInt(String assocDefUri) { 125 return getTopic(assocDefUri).getSimpleValue().intValue(); 126 } 127 128 @Override 129 public Integer getIntOrNull(String assocDefUri) { 130 Topic topic = getTopicOrNull(assocDefUri); 131 return topic != null ? topic.getSimpleValue().intValue() : null; 132 } 133 134 @Override 135 public long getLong(String assocDefUri) { 136 return getTopic(assocDefUri).getSimpleValue().longValue(); 137 } 138 139 @Override 140 public Long getLongOrNull(String assocDefUri) { 141 Topic topic = getTopicOrNull(assocDefUri); 142 return topic != null ? topic.getSimpleValue().longValue() : null; 143 } 144 145 @Override 146 public double getDouble(String assocDefUri) { 147 return getTopic(assocDefUri).getSimpleValue().doubleValue(); 148 } 149 150 @Override 151 public Double getDoubleOrNull(String assocDefUri) { 152 Topic topic = getTopicOrNull(assocDefUri); 153 return topic != null ? topic.getSimpleValue().doubleValue() : null; 154 } 155 156 @Override 157 public boolean getBoolean(String assocDefUri) { 158 return getTopic(assocDefUri).getSimpleValue().booleanValue(); 159 } 160 161 @Override 162 public Boolean getBooleanOrNull(String assocDefUri) { 163 Topic topic = getTopicOrNull(assocDefUri); 164 return topic != null ? topic.getSimpleValue().booleanValue() : null; 165 } 166 167 @Override 168 public Object getObject(String assocDefUri) { 169 return getTopic(assocDefUri).getSimpleValue().value(); 170 } 171 172 @Override 173 public Object getObjectOrNull(String assocDefUri) { 174 Topic topic = getTopicOrNull(assocDefUri); 175 return topic != null ? topic.getSimpleValue().value() : null; 176 } 177 178 // --- 179 180 @Override 181 public ChildTopics getChildTopics(String assocDefUri) { 182 return getTopic(assocDefUri).getChildTopics(); 183 } 184 185 // Note: there are no convenience accessors for a multiple-valued child. 186 187 188 189 // === Manipulators === 190 191 // --- Single-valued Childs --- 192 193 @Override 194 public ChildTopics set(String assocDefUri, TopicModel value) { 195 return _updateOne(assocDefUri, mf.newRelatedTopicModel(value)); 196 } 197 198 // --- 199 200 @Override 201 public ChildTopics set(String assocDefUri, Object value) { 202 return _updateOne(assocDefUri, mf.newRelatedTopicModel(mf.childTypeUri(assocDefUri), new SimpleValue(value))); 203 } 204 205 @Override 206 public ChildTopics set(String assocDefUri, ChildTopicsModel value) { 207 return _updateOne(assocDefUri, mf.newRelatedTopicModel(mf.childTypeUri(assocDefUri), value)); 208 } 209 210 // --- 211 212 @Override 213 public ChildTopics setRef(String assocDefUri, long refTopicId) { 214 return _updateOne(assocDefUri, mf.newTopicReferenceModel(refTopicId)); 215 } 216 217 @Override 218 public ChildTopics setRef(String assocDefUri, long refTopicId, ChildTopicsModel relatingAssocChildTopics) { 219 return _updateOne(assocDefUri, mf.newTopicReferenceModel(refTopicId, relatingAssocChildTopics)); 220 } 221 222 @Override 223 public ChildTopics setRef(String assocDefUri, String refTopicUri) { 224 return _updateOne(assocDefUri, mf.newTopicReferenceModel(refTopicUri)); 225 } 226 227 @Override 228 public ChildTopics setRef(String assocDefUri, String refTopicUri, ChildTopicsModel relatingAssocChildTopics) { 229 return _updateOne(assocDefUri, mf.newTopicReferenceModel(refTopicUri, relatingAssocChildTopics)); 230 } 231 232 // --- 233 234 @Override 235 public ChildTopics setDeletionRef(String assocDefUri, long refTopicId) { 236 return _updateOne(assocDefUri, mf.newTopicDeletionModel(refTopicId)); 237 } 238 239 @Override 240 public ChildTopics setDeletionRef(String assocDefUri, String refTopicUri) { 241 return _updateOne(assocDefUri, mf.newTopicDeletionModel(refTopicUri)); 242 } 243 244 // --- Multiple-valued Childs --- 245 246 @Override 247 public ChildTopics add(String assocDefUri, TopicModel value) { 248 return _updateMany(assocDefUri, mf.newRelatedTopicModel(value)); 249 } 250 251 // --- 252 253 @Override 254 public ChildTopics add(String assocDefUri, Object value) { 255 return _updateMany(assocDefUri, mf.newRelatedTopicModel(mf.childTypeUri(assocDefUri), new SimpleValue(value))); 256 } 257 258 @Override 259 public ChildTopics add(String assocDefUri, ChildTopicsModel value) { 260 return _updateMany(assocDefUri, mf.newRelatedTopicModel(mf.childTypeUri(assocDefUri), value)); 261 } 262 263 // --- 264 265 @Override 266 public ChildTopics addRef(String assocDefUri, long refTopicId) { 267 return _updateMany(assocDefUri, mf.newTopicReferenceModel(refTopicId)); 268 } 269 270 @Override 271 public ChildTopics addRef(String assocDefUri, long refTopicId, ChildTopicsModel relatingAssocChildTopics) { 272 return _updateMany(assocDefUri, mf.newTopicReferenceModel(refTopicId, relatingAssocChildTopics)); 273 } 274 275 @Override 276 public ChildTopics addRef(String assocDefUri, String refTopicUri) { 277 return _updateMany(assocDefUri, mf.newTopicReferenceModel(refTopicUri)); 278 } 279 280 @Override 281 public ChildTopics addRef(String assocDefUri, String refTopicUri, ChildTopicsModel relatingAssocChildTopics) { 282 return _updateMany(assocDefUri, mf.newTopicReferenceModel(refTopicUri, relatingAssocChildTopics)); 283 } 284 285 // --- 286 287 @Override 288 public ChildTopics addDeletionRef(String assocDefUri, long refTopicId) { 289 return _updateMany(assocDefUri, mf.newTopicDeletionModel(refTopicId)); 290 } 291 292 @Override 293 public ChildTopics addDeletionRef(String assocDefUri, String refTopicUri) { 294 return _updateMany(assocDefUri, mf.newTopicDeletionModel(refTopicUri)); 295 } 296 297 298 299 // === Iterable Implementation === 300 301 @Override 302 public Iterator<String> iterator() { 303 return model.iterator(); 304 } 305 306 // ----------------------------------------------------------------------------------------- Package Private Methods 307 308 boolean has(String assocDefUri) { 309 return model.has(assocDefUri); 310 } 311 312 int size() { 313 return model.size(); 314 } 315 316 // ------------------------------------------------------------------------------------------------- Private Methods 317 318 // Note 1: we need to explicitly declare the arg as RelatedTopicModel. When declared as TopicModel instead the 319 // JVM would invoke the ChildTopicsModel's put()/add() which takes a TopicModel object even if at runtime a 320 // RelatedTopicModel or even a TopicReferenceModel is passed. This is because Java method overloading involves 321 // no dynamic dispatch. See the methodOverloading tests in JavaAPITest.java (in module dmx-test). ### still true? 322 323 // Note 2: calling parent.update(..) would not work. The JVM would call the update() method of the base class 324 // (DMXObjectImpl), not the subclass's update() method. This is related to Java's (missing) multiple 325 // dispatch. Note that 2 inheritance hierarchies are involved here: the DM object hierarchy and the DM model 326 // hierarchy. See the missingMultipleDispatch tests in JavaAPITest.java (in module dmx-test). ### still true? 327 328 private ChildTopics _updateOne(String assocDefUri, RelatedTopicModel newChildTopic) { 329 parent.updateChildTopics(mf.newChildTopicsModel().put(assocDefUri, newChildTopic)); 330 return this; 331 } 332 333 private ChildTopics _updateMany(String assocDefUri, RelatedTopicModel newChildTopic) { 334 parent.updateChildTopics(mf.newChildTopicsModel().add(assocDefUri, newChildTopic)); 335 return this; 336 } 337 338 // --- 339 340 /** 341 * Loads the child topics for the given assoc def, provided they are not loaded already. 342 */ 343 private void loadChildTopics(String assocDefUri) { 344 parent.loadChildTopics(assocDefUri, false); // deep=false, FIXME? 345 } 346 347 348 349 // === Instantiation === 350 351 private RelatedTopic _getTopic(String assocDefUri) { 352 return instantiate(model.getTopic(assocDefUri)); 353 } 354 355 private RelatedTopic _getTopicOrNull(String assocDefUri) { 356 RelatedTopicModel topic = model.getTopicOrNull(assocDefUri); 357 return topic != null ? instantiate(topic) : null; 358 } 359 360 // --- 361 362 private List<RelatedTopic> _getTopics(String assocDefUri) { 363 return instantiate(model.getTopics(assocDefUri)); 364 } 365 366 private List<RelatedTopic> _getTopicsOrNull(String assocDefUri) { 367 List<? extends RelatedTopicModel> topics = model.getTopicsOrNull(assocDefUri); 368 return topics != null ? instantiate(topics) : null; 369 } 370 371 // --- 372 373 private List<RelatedTopic> instantiate(List<? extends RelatedTopicModel> models) { 374 List<RelatedTopic> topics = new ArrayList(); 375 for (RelatedTopicModel model : models) { 376 topics.add(instantiate(model)); 377 } 378 return topics; 379 } 380 381 private RelatedTopic instantiate(RelatedTopicModel model) { 382 try { 383 return new RelatedTopicImpl((RelatedTopicModelImpl) model, pl); 384 } catch (Exception e) { 385 throw new RuntimeException("Instantiating a RelatedTopic failed (" + model + ")", e); 386 } 387 } 388}