001package org.biopax.paxtools.impl.level3;
002
003import org.apache.commons.logging.Log;
004import org.apache.commons.logging.LogFactory;
005import org.biopax.paxtools.util.*;
006import org.biopax.paxtools.impl.BioPAXElementImpl;
007import org.biopax.paxtools.model.BioPAXElement;
008import org.biopax.paxtools.model.level3.*;
009import org.biopax.paxtools.util.SetEquivalenceChecker;
010import org.hibernate.annotations.Cache;
011import org.hibernate.annotations.CacheConcurrencyStrategy;
012import org.hibernate.annotations.Proxy;
013import org.hibernate.annotations.DynamicInsert;
014import org.hibernate.annotations.DynamicUpdate; 
015import org.hibernate.search.annotations.*;
016
017import javax.persistence.JoinTable;
018import javax.persistence.ManyToMany;
019import javax.persistence.ManyToOne;
020import javax.persistence.Transient;
021import java.util.Set;
022
023@javax.persistence.Entity
024@Proxy(proxyClass=PhysicalEntity.class)
025@Indexed
026@Boost(1.3f)
027@FullTextFilterDefs( { //these filters are global (can be defined on any @Indexed entity), names - unique!
028    @FullTextFilterDef(name = BioPAXElementImpl.FILTER_BY_ORGANISM, impl = OrganismFilterFactory.class), 
029    @FullTextFilterDef(name = BioPAXElementImpl.FILTER_BY_DATASOURCE, impl = DataSourceFilterFactory.class) 
030})
031@DynamicUpdate @DynamicInsert
032@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
033public class PhysicalEntityImpl extends EntityImpl implements PhysicalEntity
034{
035        private CellularLocationVocabulary cellularLocation;
036        private Set<PhysicalEntity> memberPhysicalEntity;
037        private Set<Complex> componentOf;
038        private Set<EntityFeature> feature;
039        private Set<EntityFeature> notFeature;
040        private Set<Control> controllerOf;
041        private final Log log = LogFactory.getLog(PhysicalEntityImpl.class);
042        private Set<PhysicalEntity> memberPhysicalEntityOf;
043
044        public PhysicalEntityImpl()
045        {
046                feature = BPCollections.I.createSafeSet();
047                notFeature = BPCollections.I.createSafeSet();
048                controllerOf = BPCollections.I.createSafeSet();
049                componentOf = BPCollections.I.createSafeSet();
050                memberPhysicalEntityOf = BPCollections.I.createSafeSet(); //TODO make generic?
051                memberPhysicalEntity = BPCollections.I.createSafeSet();
052        }
053
054        @Transient
055        public Class<? extends PhysicalEntity> getModelInterface()
056        {
057                return PhysicalEntity.class;
058        }
059
060        @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
061        @ManyToMany(targetEntity = ComplexImpl.class, mappedBy = "component")
062        public Set<Complex> getComponentOf()
063        {
064                return componentOf;
065        }
066
067        @ManyToOne(targetEntity = CellularLocationVocabularyImpl.class)
068        public CellularLocationVocabulary getCellularLocation()
069        {
070                return cellularLocation;
071        }
072
073        public void setCellularLocation(CellularLocationVocabulary location)
074        {
075                this.cellularLocation = location;
076        }
077
078        @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
079        @ManyToMany(targetEntity = EntityFeatureImpl.class)
080        @JoinTable(name="feature")
081        public Set<EntityFeature> getFeature()
082        {
083                return feature;
084        }
085
086        public void addFeature(EntityFeature feature)
087        {
088                if (feature != null) {
089                        checkAndAddFeature(feature, feature.getFeatureOf());
090                        this.feature.add(feature);
091                }
092        }
093
094
095        public void removeFeature(EntityFeature feature)
096        {
097                if (feature != null) {
098                        synchronized (feature) {
099                                assert feature.getFeatureOf().contains(this) ^ feature.getNotFeatureOf().contains(this) 
100                                : feature + " is both 'feature' and 'notFeature' of " + this; 
101                                //TODO even if the assertion fails, so what? (in any case, we still want to remove this PE from the set)
102                                feature.getFeatureOf().remove(this);
103                        }
104                        this.feature.remove(feature);
105                }
106        }
107
108        protected void setFeature(Set<EntityFeature> feature)
109        {
110                this.feature = feature;
111        }
112
113        @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
114        @ManyToMany(targetEntity = EntityFeatureImpl.class)
115        @JoinTable(name="notfeature")
116        public Set<EntityFeature> getNotFeature()
117        {
118                return notFeature;
119        }
120
121
122        public void addNotFeature(EntityFeature feature)
123        {
124                if (feature != null) {
125                        checkAndAddFeature(feature, feature.getNotFeatureOf());
126                        this.notFeature.add(feature);
127                }
128        }
129
130        public void removeNotFeature(EntityFeature feature)
131        {
132                if (feature != null) {
133                        synchronized (feature) {
134                                assert feature.getFeatureOf().contains(this) ^ feature.getNotFeatureOf().contains(this) 
135                                : feature + " is both 'feature' and 'notFeature' of " + this; 
136                                //TODO even if the assertion fails, so what? (in any case, we still want to remove this PE from the set)
137                                feature.getNotFeatureOf().remove(this);
138                        }
139                        this.notFeature.remove(feature);
140                }
141        }
142
143        protected void setNotFeature(Set<EntityFeature> featureSet)
144        {
145                this.notFeature = featureSet;
146        }
147
148
149        @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
150        @ManyToMany(targetEntity = PhysicalEntityImpl.class)
151        @JoinTable(name="memberPhysicalEntity")         
152        public Set<PhysicalEntity> getMemberPhysicalEntity()
153        {
154                return this.memberPhysicalEntity;    //TODO (what?)
155        }
156
157        public void addMemberPhysicalEntity(PhysicalEntity newMember)
158        {
159                if (newMember != null) {
160                        this.memberPhysicalEntity.add(newMember);
161                        synchronized (newMember) {
162                                newMember.getMemberPhysicalEntityOf().add(this);
163                        }
164                }
165        }
166
167        public void removeMemberPhysicalEntity(PhysicalEntity oldMember)
168        {
169                if (oldMember != null) {
170                        this.memberPhysicalEntity.remove(oldMember);
171                        synchronized (oldMember) {
172                                oldMember.getMemberPhysicalEntityOf().remove(this);
173                        }
174                }
175        }
176
177        protected void setMemberPhysicalEntity(Set<PhysicalEntity> memberPhysicalEntity)
178        {
179                this.memberPhysicalEntity = memberPhysicalEntity; //TODO (what?)
180        }
181
182
183    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
184        @ManyToMany(targetEntity = PhysicalEntityImpl.class, mappedBy = "memberPhysicalEntity")
185        public Set<PhysicalEntity> getMemberPhysicalEntityOf()
186        {
187                return memberPhysicalEntityOf;
188        }
189
190
191        private void checkAndAddFeature(EntityFeature feature,
192                                        Set<PhysicalEntity> target)
193        {
194                if (feature.getFeatureOf().contains(this) ||
195                    feature.getNotFeatureOf().contains(this))
196                {
197                        if(log.isWarnEnabled())
198                                log.warn("Redundant attempt to set the inverse link! " 
199                                                + " this " + getModelInterface().getSimpleName() 
200                                                + " " + getRDFId() + " and - " 
201                                                + feature.getModelInterface().getSimpleName() + " "
202                                                + feature.getRDFId());
203                }
204                target.add(this);
205        }
206
207        // --------------------- Interface BioPAXElement ---------------------
208
209
210        @Override
211        protected boolean semanticallyEquivalent(BioPAXElement element)
212        {
213                if (!(element instanceof PhysicalEntity))
214                        return false;
215
216                PhysicalEntity that = (PhysicalEntity) element;
217                return hasEquivalentCellularLocation(that)
218                       && hasEquivalentFeatures(that)
219                       && SetEquivalenceChecker
220                                .isEquivalent(getMemberPhysicalEntity(), that.getMemberPhysicalEntity())
221                       &&
222                       super.semanticallyEquivalent(element); // StackOverflow BUG fixed: was isEquivalent !
223        }
224
225        @Override
226        public int equivalenceCode()
227        {
228                return hashCode();
229        }
230
231        public boolean hasEquivalentCellularLocation(PhysicalEntity that)
232        {
233                boolean equivalency = false;
234                if (that != null)
235                {
236                        equivalency = (cellularLocation != null)
237                                      ? cellularLocation.isEquivalent(that.getCellularLocation())
238                                      : that.getCellularLocation() == null;
239                }
240                return equivalency;
241        }
242
243        public boolean hasEquivalentFeatures(PhysicalEntity that)
244        {
245                boolean equivalency = false;
246                if (that != null)
247                {
248                        equivalency =
249                                        SetEquivalenceChecker.isEquivalent(this.getFeature(), that.getFeature()) &&
250                                        SetEquivalenceChecker.isEquivalent(this.getNotFeature(), that.getNotFeature());
251                }
252                return equivalency;
253        }
254
255        protected int locationAndFeatureCode()
256        {
257                int result = (cellularLocation != null ? cellularLocation.hashCode() : 0);
258                result = 31 * result + (feature != null ? feature.hashCode() : 0);
259                result = 31 * result + (notFeature != null ? notFeature.hashCode() : 0);
260                return result;
261
262        }
263
264        @ManyToMany(targetEntity = ControlImpl.class, mappedBy = "peController")
265        public Set<Control> getControllerOf()
266        {
267                return controllerOf;
268        }
269
270        protected void setControllerOf(Set<Control> controllerOf)
271        {
272                this.controllerOf = controllerOf;
273        }
274
275        protected void setMemberPhysicalEntityOf(Set<PhysicalEntity> memberPhysicalEntityOf)
276        {
277                this.memberPhysicalEntityOf = memberPhysicalEntityOf;
278        }
279
280        protected void setComponentOf(Set<Complex> componentOf)
281        {
282                this.componentOf = componentOf;
283        }
284
285}