001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 * Created on Jun 4, 2010
021 * Author: Jianjiong Gao
022 *
023 */
024
025package org.biojava.nbio.protmod;
026
027import java.util.*;
028
029/**
030 * contains information about a certain Component.
031 * The Component class uses the extensible enum pattern.
032 * You can't instantiate Component directly, instead
033 * you have to use one of the {@link register} and {@link of} methods.
034 *
035 * @author Jianjiong Gao
036 * @since 3.0
037 */
038public final class Component {
039        private final Set<String> pdbccIds;
040        private final boolean isNTerminal;
041        private final boolean isCTerminal;
042
043        private static Set<Component> components = null;
044        private static Map<Set<String>, Component> nonTerminalComps = null;
045        private static Map<Set<String>, Component> nTerminalAminoAcids = null;
046        private static Map<Set<String>, Component> cTerminalAminoAcids = null;
047
048        /**
049         * Lazy initialization of the static variables.
050         */
051        private static void lazyInit() {
052                if (components==null) {
053                        components = new HashSet<Component>();
054                        nonTerminalComps = new HashMap<Set<String>, Component>();
055                        nTerminalAminoAcids = new HashMap<Set<String>, Component>();
056                        cTerminalAminoAcids = new HashMap<Set<String>, Component>();
057                }
058        }
059
060        /**
061         * Create a ComponentImpl.
062         * @param pdbccIds a set of possible Protein Data Bank ID. Cannot be null or empty.
063         * @param isNTerminal true if occurring at N-terminal. false, otherwise.
064         * @param isCTerminal true if occurring at C-terminal. false, otherwise.
065         * @throws IllegalArgumentException if pdbccId or type is null,
066         *  or terminal condition is indicated for non-amino-acid component,
067         *  or both N-terminal and C-terminal are true.
068         */
069        private Component(final Set<String> pdbccIds,
070                        final boolean isNTerminal, final boolean isCTerminal) {
071                if (pdbccIds==null || pdbccIds.isEmpty()) {
072                        throw new IllegalArgumentException("pdbccId or type cannot be null.");
073                }
074
075                if (isNTerminal&&isCTerminal) {
076                        throw new IllegalArgumentException("An amino acid can be specified at" +
077                                        "N-terminal or C-terminal but not both."); //TODO: is this true?
078                }
079
080                this.pdbccIds = pdbccIds;
081                this.isNTerminal = isNTerminal;
082                this.isCTerminal = isCTerminal;
083        }
084
085        /**
086         *
087         * @return Protein Data Bank ID.
088         */
089        public Set<String> getPdbccIds() {
090                return pdbccIds;
091        }
092
093        /**
094         *
095         * @return true if occurring on N terminal; false, otherwise.
096         */
097        public boolean isNTerminal() {
098                return isNTerminal;
099        }
100
101        /**
102         *
103         * @return true if occurring on C terminal; false, other wise.
104         */
105        public boolean isCTerminal() {
106                return isCTerminal;
107        }
108
109        /**
110         * Get a Component that does not have to occur at terminals. If the
111         * corresponding component has already been registered, return that one.
112         * @param pdbccIds possible Protein Data Bank ID.
113         * @return a component.
114         * @throws IllegalArgumentException if pdbccId or type is null,
115         *  or the pdbccId has been registered as a different type.
116         */
117        public static Component of(final String pdbccId) {
118                return of(pdbccId, false, false);
119        }
120
121        /**
122         * Get or create a Component.
123         * @param pdbccId Protein Data Bank ID.
124         * @param isNTerminal true if occurring at N-terminal. false, otherwise.
125         * @param isCTerminal true if occurring at C-terminal. false, otherwise.
126         * @return a component.
127         * @throws IllegalArgumentException if pdbccId or type is null,
128         *  or the pdbccId has been registered as a different type,
129         *  or terminal condition is indicated for non-amino-acid component,
130         *  or both N-terminal and C-terminal are true.
131         */
132        public static Component of(final String pdbccId,
133                        final boolean isNTerminal, final boolean isCTerminal) {
134                return of (Collections.singleton(pdbccId), isNTerminal, isCTerminal);
135        }
136
137        /**
138         * Get a Component that does not have to occur at terminals. If the
139         * corresponding component has already been registered, return that one.
140         * @param pdbccIds a set of possible Protein Data Bank ID.
141         * @return a component.
142         * @throws IllegalArgumentException if pdbccId or type is null,
143         *  or the pdbccId has been registered as a different type.
144         */
145        public static Component of(final Set<String> pdbccIds) {
146                return of(pdbccIds, false, false);
147        }
148
149        /**
150         * Get or create a Component.
151         * @param pdbccIds a set of possible Protein Data Bank ID.
152         * @param isNTerminal true if occurring at N-terminal. false, otherwise.
153         * @param isCTerminal true if occurring at C-terminal. false, otherwise.
154         * @return a component.
155         * @throws IllegalArgumentException if pdbccId or type is null,
156         *  or the pdbccId has been registered as a different type,
157         *  or terminal condition is indicated for non-amino-acid component,
158         *  or both N-terminal and C-terminal are true.
159         */
160        public static Component of(final Set<String> pdbccIds,
161                        final boolean isNTerminal, final boolean isCTerminal) {
162                if (isNTerminal && isCTerminal) {
163                        throw new IllegalArgumentException("An amino acid can be at" +
164                        "N-terminal or C-terminal but not both."); //TODO: is this true?
165                }
166
167                lazyInit();
168
169                Component comp;
170                if (isNTerminal) {
171                        comp = nTerminalAminoAcids.get(pdbccIds);
172                        if (comp == null) {
173                                comp = new Component(pdbccIds, isNTerminal, isCTerminal);
174                                nTerminalAminoAcids.put(pdbccIds, comp);
175                        }
176                } else if (isCTerminal) {
177                        comp = cTerminalAminoAcids.get(pdbccIds);
178                        if (comp == null) {
179                                comp = new Component(pdbccIds, isNTerminal, isCTerminal);
180                                cTerminalAminoAcids.put(pdbccIds, comp);
181                        }
182                } else {
183                        comp = nonTerminalComps.get(pdbccIds);
184                        if (comp == null) {
185                                comp = new Component(pdbccIds, isNTerminal, isCTerminal);
186                                nonTerminalComps.put(pdbccIds, comp);
187                        }
188                }
189
190                components.add(comp);
191                return comp;
192        }
193
194        public static Set<Component> allComponents() {
195                return Collections.unmodifiableSet(components);
196        }
197
198        /**
199         *
200         * @return informative description.
201         */
202        @Override
203        public String toString() {
204                StringBuilder sb = new StringBuilder();
205                sb.append(getPdbccIds());
206                if (isCTerminal()) {
207                        sb.append("(C)");
208                } else if (isNTerminal()) {
209                        sb.append("(N)");
210                }
211                return sb.toString();
212        }
213}