001package org.biopax.paxtools.util; 002 003import org.apache.commons.logging.Log; 004import org.apache.commons.logging.LogFactory; 005import org.biopax.paxtools.model.BioPAXElement; 006 007import java.util.*; 008 009 010/** 011 * A thread-safe set of BioPAX objects that also prevents adding several elements 012 * having the same URI. It also allows to quickly get a BioPAX 013 * object by URI. This set is used internally by all multiple cardinality 014 * BioPAX object property and inverse property implementations (since v4.2, 2013). 015 * 016 * @author rodche 017 */ 018public class BiopaxSafeSet<E extends BioPAXElement> extends AbstractSet<E> 019{ 020 private final static Log LOG = LogFactory.getLog(BiopaxSafeSet.class); 021 022 //initial map is to be reset to a modifiable instance on first write 023 private final static Map empty = Collections.unmodifiableMap(Collections.emptyMap()); 024 025 private Map<String,E> map; 026 027 public BiopaxSafeSet() 028 { 029 map = empty; 030 } 031 032 public Iterator<E> iterator() { 033 synchronized (map) { 034 return map.values().iterator(); 035 } 036 } 037 038 public int size() 039 { 040 synchronized (map) { 041 return map.size(); 042 } 043 } 044 045 @Override 046 public boolean add(E bpe) 047 { 048 synchronized (map) { 049 if(map.isEmpty()) 050 { //new real map instead of initial fake (empty) one 051 this.map = BPCollections.I.createMap(); 052 } 053 } 054 055 String uri = bpe.getRDFId(); 056 057 synchronized (map) { //sync on the new map instance 058 if (!map.containsKey(uri)) { 059 map.put(uri, bpe); 060 return true; 061 } else { 062 // do not throw an ex., because duplicate attempts occur naturally 063 // (e.g., same PE on both left and right sides of a reaction 064 // causes same participant/participantOf is touched twice) 065 LOG.debug("ignored duplicate:" + uri); 066 return false; 067 } 068 } 069 } 070 071 072 @Override 073 public boolean contains(Object o) { 074 if(map==empty) 075 return false; 076 077 synchronized (map) {//to sync due to two operations 078 return super.contains(o) 079 && ( get(((E)o).getRDFId()) == o ); 080 } 081 } 082 083 084 /** 085 * Gets a BioPAX element by URI. 086 * 087 * @param uri absolute URI of a BioPAX individual 088 * @return BioPAX object or null 089 */ 090 public E get(String uri) { 091 092 if(map==empty) 093 return null; 094 095 synchronized (map) { 096 return map.get(uri); 097 } 098 } 099}