1 /*
2  * Copyright (c) 2017 John Mayfield <jwmay@users.sf.net>
3  *
4  * Contact: cdk-devel@lists.sourceforge.net
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or (at
9  * your option) any later version. All we ask is that proper credit is given
10  * for our work, which includes - but is not limited to - adding the above
11  * copyright notice to the beginning of your source code files, and to any
12  * copyright notice that you may distribute with programs based on this work.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22  */
23 
24 package org.openscience.cdk.stereo;
25 
26 import org.openscience.cdk.interfaces.IAtom;
27 import org.openscience.cdk.interfaces.IBond;
28 import org.openscience.cdk.interfaces.IChemObject;
29 import org.openscience.cdk.interfaces.IChemObjectBuilder;
30 import org.openscience.cdk.interfaces.IStereoElement;
31 
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37 
38 abstract class AbstractStereo<F extends IChemObject, C extends IChemObject>
39     implements IStereoElement<F, C> {
40 
41     private int     value;
42     private F       focus;
43     private List<C> carriers;
44     private IChemObjectBuilder builder;
45 
numCarriers(int cfg)46     protected static int numCarriers(int cfg) {
47         return ((cfg >>> 12) & 0xf);
48     }
49 
AbstractStereo(F focus, C[] carriers, int value)50     AbstractStereo(F focus, C[] carriers, int value) {
51         if (focus == null)
52             throw new NullPointerException("Focus of stereochemistry can not be null!");
53         if (carriers == null)
54             throw new NullPointerException("Carriers of the configuration can not be null!");
55         if (carriers.length != numCarriers(value))
56             throw new IllegalArgumentException("Unexpected number of stereo carriers! expected " + ((value >>> 12) & 0xf) + " was " + carriers.length);
57         for (C carrier : carriers) {
58             if (carrier == null)
59                 throw new NullPointerException("A carrier was undefined!");
60         }
61         this.value    = value;
62         this.focus    = focus;
63         this.carriers = new ArrayList<>();
64         Collections.addAll(this.carriers, carriers);
65     }
66 
67     /**
68      * {@inheritDoc}
69      */
70     @Override
getFocus()71     public F getFocus() {
72         return focus;
73     }
74 
75     /**
76      * {@inheritDoc}
77      */
78     @Override
getCarriers()79     public List<C> getCarriers() {
80         return carriers;
81     }
82 
83     /**
84      * {@inheritDoc}
85      */
86     @Override
getConfigClass()87     public int getConfigClass() {
88         return value & CLS_MASK;
89     }
90 
91     /**
92      * {@inheritDoc}
93      */
94     @Override
getConfigOrder()95     public int getConfigOrder() {
96         return value & CFG_MASK;
97     }
98 
99     /**
100      * {@inheritDoc}
101      */
102     @Override
getConfig()103     public int getConfig() {
104         return value;
105     }
106 
107     /**
108      * {@inheritDoc}
109      */
110     @Override
setConfigOrder(int cfg)111     public void setConfigOrder(int cfg) {
112         value = getConfigClass() | cfg;
113     }
114 
115     /**
116      * {@inheritDoc}
117      */
118     @Override
contains(IAtom atom)119     public boolean contains(IAtom atom) {
120         if (focus.equals(atom) || (focus instanceof IBond && ((IBond) focus).contains(atom)))
121             return true;
122         for (C carrier : carriers) {
123             if (carrier.equals(atom) ||
124                 (carrier instanceof IBond && ((IBond) carrier).contains(atom)))
125                 return true;
126         }
127         return false;
128     }
129 
130     /**
131      * {@inheritDoc}
132      */
133     @Override
map(Map<IAtom, IAtom> atoms, Map<IBond, IBond> bonds)134     public IStereoElement<F,C> map(Map<IAtom, IAtom> atoms,
135                                    Map<IBond, IBond> bonds) {
136         if (atoms == null)
137             throw new IllegalArgumentException("Atom map should be non-null");
138         if (bonds == null)
139             throw new IllegalArgumentException("Bond map should be non-null");
140         Map<IChemObject,IChemObject> map = new HashMap<>(atoms.size() + bonds.size());
141         map.putAll(atoms);
142         map.putAll(bonds);
143         return map(map);
144     }
145 
146     @Override
147     @SuppressWarnings("unchecked")
map(Map<IChemObject, IChemObject> chemobjs)148     public IStereoElement<F, C> map(Map<IChemObject, IChemObject> chemobjs) {
149         if (chemobjs == null)
150             throw new NullPointerException("chemobj map was not provided!");
151         F newfocus = (F) chemobjs.get(focus);
152         if (newfocus == null)
153             newfocus = focus;
154         List<C> newcarriers = carriers;
155         for (int i = 0; i < newcarriers.size(); i++) {
156             C newcarrier = (C) chemobjs.get(newcarriers.get(i));
157             if (newcarrier != null) {
158                 // make a copy if this is the first change
159                 if (newcarriers == carriers)
160                     newcarriers = new ArrayList<>(carriers);
161                 newcarriers.set(i, newcarrier);
162             }
163         }
164         // no change, return self
165         if (newfocus == focus && newcarriers == carriers)
166             return this;
167         return create(newfocus, newcarriers, value);
168     }
169 
create(F focus, List<C> carriers, int cfg)170     protected abstract IStereoElement<F,C> create(F focus, List<C> carriers, int cfg);
171 
172     /**
173      *{@inheritDoc}
174      */
175     @Override
getBuilder()176     public IChemObjectBuilder getBuilder() {
177         if (builder == null)
178             throw new UnsupportedOperationException("Non-domain object!");
179         return this.builder;
180     }
181 
setBuilder(IChemObjectBuilder builder)182     protected void setBuilder(IChemObjectBuilder builder) {
183         this.builder = builder;
184     }
185 
186     // labels for describing permutation
187     protected static final int A = 0, B = 1, C = 2, D = 3, E = 4, F = 5;
188 
189     // apply the inverse of a permutation
invapply(T[] src, int[] perm)190     protected static <T> T[] invapply(T[] src, int[] perm) {
191         T[] res = src.clone();
192         for (int i = 0; i < src.length; i++)
193             res[i] = src[perm[i]];
194         return res;
195     }
196 }
197