001package org.biopax.paxtools.impl; 002 003import org.biopax.paxtools.controller.ModelUtils; 004import org.biopax.paxtools.controller.SimpleEditorMap; 005import org.biopax.paxtools.controller.SimpleMerger; 006import org.biopax.paxtools.model.BioPAXElement; 007import org.biopax.paxtools.model.BioPAXFactory; 008import org.biopax.paxtools.model.BioPAXLevel; 009import org.biopax.paxtools.model.Model; 010import org.biopax.paxtools.util.BPCollections; 011import org.biopax.paxtools.util.ClassFilterSet; 012import org.biopax.paxtools.util.IllegalBioPAXArgumentException; 013 014import java.util.*; 015 016/** 017 * This is the default implementation of the {@link Model}. Use a factory to create a model. 018 */ 019public class ModelImpl implements Model 020{ 021// ------------------------------ FIELDS ------------------------------ 022 023 private static final long serialVersionUID = -2087521863213381434L; 024 protected final Map<String, BioPAXElement> idMap; 025 private final Map<String, String> nameSpacePrefixMap; 026 private BioPAXLevel level; 027 private transient BioPAXFactory factory; 028 private final transient Set<BioPAXElement> exposedObjectSet; 029 private boolean addDependencies = false; 030 private String xmlBase; 031 032// --------------------------- CONSTRUCTORS --------------------------- 033 034 protected ModelImpl() { 035 idMap = BPCollections.I.createMap(); 036 nameSpacePrefixMap = new HashMap<String, String>(); 037 this.exposedObjectSet = new UnmodifiableImplicitSet(idMap.values()); 038 } 039 040 protected ModelImpl(BioPAXLevel level) 041 { 042 this(level.getDefaultFactory()); 043 } 044 045 public ModelImpl(BioPAXFactory factory) 046 { 047 this(); 048 this.factory = factory; 049 this.level = factory.getLevel(); 050 } 051 052// --------------------- GETTER / SETTER METHODS --------------------- 053 054 public synchronized boolean containsID(String id) { 055 return this.idMap.containsKey(id); 056 } 057 058 059 public synchronized BioPAXElement getByID(String id) { 060 BioPAXElement ret = this.idMap.get(id); 061 if(ret != null) { 062 assert ret.getRDFId().equals(id); 063 } 064 return ret; 065 } 066 067 068 public Map<String, String> getNameSpacePrefixMap() 069 { 070 return nameSpacePrefixMap; 071 } 072 073 074 private synchronized void setNameSpacePrefixMap(Map<String, String> nameSpacePrefixMap) { 075 this.nameSpacePrefixMap.clear(); 076 this.nameSpacePrefixMap.putAll(nameSpacePrefixMap); 077 } 078 079 080 public void setFactory(BioPAXFactory factory) 081 { 082 this.factory = factory; 083 this.level = factory.getLevel(); 084 } 085 086// ------------------------ INTERFACE METHODS ------------------------ 087 088// --------------------- Interface Model --------------------- 089 090// --------------------- ACCESORS and MUTATORS--------------------- 091 092 public synchronized Set<BioPAXElement> getObjects() 093 { 094 return exposedObjectSet; 095 } 096 097 public synchronized <T extends BioPAXElement> Set<T> getObjects(Class<T> filterBy) 098 { 099 return new ClassFilterSet<BioPAXElement,T>(exposedObjectSet, filterBy); 100 } 101 102 synchronized void synchronizedsetObjects(Set<BioPAXElement> objects) { 103 idMap.clear(); 104 for(BioPAXElement bpe : objects) { 105 add(bpe); 106 } 107 } 108 109 110 public synchronized void remove(BioPAXElement aBioPAXElement) 111 { 112 if(this.contains(aBioPAXElement)) 113 this.idMap.remove(aBioPAXElement.getRDFId()); 114 } 115 116 public synchronized <T extends BioPAXElement> T addNew(Class<T> c, String id) 117 { 118 T paxElement = factory.create(c, id); 119 this.add(paxElement); 120 return paxElement; 121 } 122 123 /** 124 * This method returns true if given element 125 * is the same object ("==") as the object stored in the model 126 * usually (for self-consistent models) but not necessarily under the element's ID. 127 * 128 * @param aBioPAXElement BioPAX object (individual) 129 * @return true/false - whether this model contains the object or not 130 */ 131 public synchronized boolean contains(BioPAXElement aBioPAXElement) 132 { 133 return this.idMap.get(aBioPAXElement.getRDFId()) == aBioPAXElement; 134 } 135 136// -------------------------- OTHER METHODS -------------------------- 137 138 public synchronized void add(BioPAXElement aBioPAXElement) 139 { 140 String rdfId = aBioPAXElement.getRDFId(); 141 if(!this.level.hasElement(aBioPAXElement)) 142 { 143 throw new IllegalBioPAXArgumentException( 144 "Given object is of wrong level"); 145 } 146 147 if (rdfId == null) 148 { 149 throw new IllegalBioPAXArgumentException( 150 "null ID: every object must have an RDF ID"); 151 } 152 else if (this.idMap.containsKey(rdfId)) 153 { 154 throw new IllegalBioPAXArgumentException( 155 "I already have an object with the same ID: " + rdfId + 156 ". Try removing it first"); 157 } 158 else if (this.contains(aBioPAXElement)) 159 { 160 throw new IllegalBioPAXArgumentException( 161 "duplicate element:" + aBioPAXElement); 162 } 163 else 164 { 165 this.idMap.put(rdfId, aBioPAXElement); 166 } 167 } 168 169 170 public BioPAXLevel getLevel() 171 { 172 return level; 173 } 174 175 // used by hibernate 176 synchronized void setLevel(BioPAXLevel level) { 177 this.level = level; 178 this.factory = level.getDefaultFactory(); 179 } 180 181 182 public void setAddDependencies(boolean value) { 183 this.addDependencies = value; 184 } 185 186 187 public boolean isAddDependencies() { 188 return addDependencies; 189 } 190 191 private class UnmodifiableImplicitSet implements Set<BioPAXElement> 192 { 193 private final Collection<BioPAXElement> elements; 194 195 public UnmodifiableImplicitSet( 196 Collection<BioPAXElement> elements) 197 { 198 199 this.elements = elements; 200 } 201 202 public int size() 203 { 204 return elements.size(); 205 } 206 207 public boolean isEmpty() 208 { 209 return elements.isEmpty(); 210 } 211 212 public boolean contains(Object o) 213 { 214 return elements.contains(o); 215 } 216 217 public Iterator<BioPAXElement> iterator() 218 { 219 return elements.iterator(); 220 } 221 222 public Object[] toArray() 223 { 224 return elements.toArray(); 225 } 226 227 public <T> T[] toArray(T[] a) 228 { 229 return elements.toArray(a); 230 } 231 232 public boolean add(BioPAXElement bioPAXElement) 233 { 234 throw new UnsupportedOperationException(); 235 } 236 237 public boolean remove(Object o) 238 { 239 throw new UnsupportedOperationException(); 240 } 241 242 public boolean containsAll(Collection<?> c) 243 { 244 return elements.containsAll(c); 245 } 246 247 public boolean addAll(Collection<? extends BioPAXElement> c) 248 { 249 throw new UnsupportedOperationException(); 250 } 251 252 public boolean retainAll(Collection<?> c) 253 { 254 throw new UnsupportedOperationException(); 255 } 256 257 public boolean removeAll(Collection<?> c) 258 { 259 throw new UnsupportedOperationException(); 260 } 261 262 public void clear() 263 { 264 throw new UnsupportedOperationException(); 265 } 266 } 267 268 /** 269 * It does not automatically replace or clean up the old 270 * element's object properties, therefore, some child 271 * elements may become "dangling" if they were used by 272 * the replaced element only. 273 * 274 * Can also clear object properties (- replace with null). 275 */ 276 public synchronized void replace(final BioPAXElement existing, final BioPAXElement replacement) 277 { 278 ModelUtils.replace(this, Collections.singletonMap(existing, replacement)); 279 remove(existing); 280 if(replacement != null) 281 add(replacement); 282 } 283 284 285 /** 286 * This is default implementation that uses the 287 * id-based merging ({@link SimpleMerger#merge(Model, Model...)}) 288 * 289 * NOTE: some applications, such as those dealing with persistence/transactions 290 * or advanced BioPAX alignment/comparison algorithms (like the Patch), 291 * may have to implement and use a more specific method instead. 292 * 293 * @see SimpleMerger 294 * @see Model#merge(Model) 295 */ 296 public synchronized void merge(Model source) { 297 SimpleMerger merger = new SimpleMerger( 298 SimpleEditorMap.get(level)); 299 if(source == null) 300 merger.merge(this, this); //repairs itself 301 else 302 merger.merge(this, source); 303 } 304 305 306 /** 307 * 308 * This implementation "repairs" the model 309 * without unnecessarily copying objects: 310 * - recursively adds lost "children" (not null object property values 311 * for which {@link Model#contains(BioPAXElement)} returns False) 312 * - updates object properties (should refer to model's elements) 313 * 314 */ 315 @Override 316 public synchronized void repair() { 317 // updates props and children 318 merge(null); 319 } 320 321 @Override 322 public void setXmlBase(String base) { 323 this.xmlBase = base; 324 } 325 326 @Override 327 public String getXmlBase() { 328 return this.xmlBase; 329 } 330}