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