001    /*
002     * Copyright (c) 1998-2014 ChemAxon Ltd. All Rights Reserved.
003     *
004     * This software is the confidential and proprietary information of
005     * ChemAxon. You shall not disclose such Confidential Information
006     * and shall use it only in accordance with the terms of the agreements
007     * you entered into with ChemAxon.
008     *
009     */
010    
011    package com.chemaxon.descriptors.fingerprints.pf2d;
012    
013    import chemaxon.descriptors.PFParameters;
014    import chemaxon.struc.Molecule;
015    import com.chemaxon.descriptors.common.Descriptor;
016    import com.chemaxon.descriptors.common.DescriptorComparator;
017    import com.chemaxon.descriptors.common.DescriptorGenerator;
018    import com.chemaxon.descriptors.common.DescriptorParameters;
019    import com.chemaxon.descriptors.common.FloatVectorDescriptor;
020    import java.io.InvalidObjectException;
021    import java.io.ObjectInputStream;
022    import java.io.Serializable;
023    
024    /**
025     * Create a thread unsafe but ThreadLocal hacked Pd generator.
026     *
027     * <p>We hope efficiency gain from this dangerous workaround. <b>Use with extreme care!</b>.</p>
028     *
029     * <p>Safe(e) usage rules of thumb:
030     * <ul><li>Do not hold reference to instances of this class in a static context</li>
031     * <li>Make instances inaccessable periodically allowing GC to release thread associated references</li></ul>
032     * </p>
033     *
034     * <p>The API contracts of {@link DescriptorGenerator} regarding API discovery are not fully satisfied by this
035     * class.</p>
036     *
037     * <p>The guard objects associated to the generated descriptors is the reference of the wrapped generator. For details
038     * see API contracts of {@link Descriptor#getDescriptorGenerator()}</p>
039     *
040     * <p><b>Beware that classes {@link chemaxon.descriptors.PFParameters} and
041     * {@link com.chemaxon.descriptors.fingerprints.pf2d.PfParameters} have very similar name and they are used in explicit
042     * casting in the code!</b></p>
043     *
044     * @author Gabor Imre
045     */
046    public final class ThreadLocalizedPfGenerator implements DescriptorGenerator<FloatVectorDescriptor> {
047    
048        /**
049         * Serial version.
050         */
051        private static final long serialVersionUID = 0;
052    
053        /**
054         * Wrapped original generator implementation.
055         */
056        private final transient PfGeneratorImpl gen;
057    
058        /**
059         * "Legacy" descriptor parameters.
060         */
061        private final transient ThreadLocal<PFParameters> threadLocalParams;
062    
063        /**
064         * Parameters object used to construct this descriptor.
065         */
066        private final transient ThreadLocalizedPfParameters params;
067    
068        /**
069         * Construct.
070         *
071         * @param gen   Generator to wrap.
072         */
073        protected ThreadLocalizedPfGenerator(ThreadLocalizedPfParameters params) {
074            this.params = params;
075    
076            this.gen = params.constructPdGeneratorImpl();
077            this.threadLocalParams = new ThreadLocal<PFParameters>() {
078    
079                // TODO: override PfParameters PfGeneratorImpl#getParameters()
080                @Override
081                public PFParameters initialValue() {
082                    return PfGeneratorImpl.createDescriptorParameters(
083                            (PfParameters) ThreadLocalizedPfGenerator.this.gen.getParameters());
084                }
085    
086            };
087        }
088    
089        @Override
090        public PharmacophoreFP generateDescriptor(Molecule mol) {
091            final PFParameters p = this.threadLocalParams.get();
092            return PfGeneratorImpl.generateDescriptor(mol, p, this.gen);
093        }
094    
095        @Override
096        public FloatVectorDescriptor getBareDescriptor(FloatVectorDescriptor descriptor) {
097            return this.gen.getBareDescriptor(descriptor);
098        }
099    
100        @Override
101        public byte[] toByteArray(FloatVectorDescriptor desc) {
102            return this.gen.toByteArray(desc);
103        }
104    
105        @Override
106        public String toString(FloatVectorDescriptor desc) {
107            return this.gen.toString(desc);
108        }
109    
110        @Override
111        public FloatVectorDescriptor fromString(String desc) {
112            return this.gen.fromString(desc);
113        }
114    
115        @Override
116        public FloatVectorDescriptor fromByteArray(byte[] desc) {
117            return this.gen.fromByteArray(desc);
118        }
119    
120        @Override
121        public DescriptorComparator<FloatVectorDescriptor> getDefaultComparator() {
122            return this.gen.getDefaultComparator();
123        }
124    
125        @Override
126        public DescriptorParameters getParameters() {
127            return this.gen.getParameters();
128        }
129    
130        @Override
131        public String toString() {
132            return "Thread localized version of " + this.gen.toString();
133        }
134    
135        @Override
136        public Object getGuardObject() {
137            return this.gen;
138        }
139    
140        // Serialization related -------------------------------------------------------------------------------------------
141        /**
142         * This method dies because {@link SerializedForm} is used through {@link #writeReplace()}.
143         *
144         * @param stream not used
145         * @throws InvalidObjectException This method should not be called
146         */
147        private void readObject(ObjectInputStream stream) throws InvalidObjectException {
148            throw new InvalidObjectException("Should be serialized in SerializedForm");
149        }
150    
151        /**
152         * Part of Java serialization mechanism.
153         *
154         * @return nominated replacement for serialization
155         *
156         * @see <a href="http://docs.oracle.com/javase/6/docs/platform/serialization/spec/output.html#5324">
157         * http://docs.oracle.com/javase/6/docs/platform/serialization/spec/output.html#5324</a>
158         */
159        private Object writeReplace() {
160            return new SerializedForm(this.params);
161        }
162    
163        /**
164         * Serialized form.
165         */
166        private static class SerializedForm implements Serializable {
167    
168            /**
169             * Serial version.
170             */
171            private static final long serialVersionUID = 0;
172    
173            /**
174             * Parameters object.
175             */
176            private final ThreadLocalizedPfParameters params;
177    
178            /**
179             * Instantiate serialized form.
180             *
181             * @param params Parameters object
182             */
183            SerializedForm(ThreadLocalizedPfParameters params) {
184                this.params = params;
185            }
186    
187            /**
188             * Part of Java serialization mechanism.
189             *
190             * @return Designated instance to return
191             *
192             * @see <a href="http://docs.oracle.com/javase/6/docs/platform/serialization/spec/input.html#5903">
193             * http://docs.oracle.com/javase/6/docs/platform/serialization/spec/input.html#5903</a>
194             */
195            Object readResolve() {
196                return this.params.getDescriptorGenerator();
197            }
198        }
199    
200    }