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    package com.chemaxon.overlap.io;
011    
012    import chemaxon.struc.Molecule;
013    import com.chemaxon.calculations.common.SubProgressObserver;
014    import com.google.common.base.Optional;
015    import com.google.common.collect.ImmutableList;
016    import java.io.PrintStream;
017    import java.util.ArrayList;
018    import java.util.List;
019    import java.util.concurrent.CancellationException;
020    import org.apache.commons.lang3.StringEscapeUtils;
021    import org.apache.commons.logging.Log;
022    
023    /**
024     * {@link MoleculeCallback} related utilities.
025     *
026     * @author Gabor Imre
027     */
028    public final class MoleculeCallbacks {
029    
030        /**
031         * No constructor exposed.
032         */
033        private MoleculeCallbacks() {}
034    
035    
036        /**
037         * Null callback.
038         *
039         * @return  A Callback doing nothing.
040         */
041        public static MoleculeCallback nullCallback() {
042            return new NullMoleculeCallback();
043        }
044    
045        /**
046         * Counting callback.
047         *
048         * @return  A counting callback with meaningful toString()
049         */
050        public static CountingMoleculeCallback countingCallback() {
051            return new CountingMoleculeCallbackImpl();
052        }
053    
054        /**
055         * Error logging callback.
056         *
057         * @param log   Logger to use
058         * @return A counting callback logging errors on a {@link Log}
059         */
060        public static CountingMoleculeCallback errorLoggingCallback(Log log) {
061            return new CountingMoleculeCallbackImpl(log);
062        }
063    
064        /**
065         * Error verbosing callback.
066         *
067         * @return A counting callback printing errors on stderr.
068         */
069        public static CountingMoleculeCallback errorVerbosingCallback() {
070            return new CountingMoleculeCallbackImpl(new Log() {
071    
072                public boolean isDebugEnabled() {
073                    throw new UnsupportedOperationException();
074                }
075    
076                public boolean isErrorEnabled() {
077                    return true;
078                }
079    
080                public boolean isFatalEnabled() {
081                    throw new UnsupportedOperationException();
082                }
083    
084                public boolean isInfoEnabled() {
085                    throw new UnsupportedOperationException();
086                }
087    
088                public boolean isTraceEnabled() {
089                    throw new UnsupportedOperationException();
090                }
091    
092                public boolean isWarnEnabled() {
093                    throw new UnsupportedOperationException();
094                }
095    
096                public void trace(Object message) {
097                    throw new UnsupportedOperationException();
098                }
099    
100                public void trace(Object message, Throwable t) {
101                    throw new UnsupportedOperationException();
102                }
103    
104                public void debug(Object message) {
105                    throw new UnsupportedOperationException();
106                }
107    
108                public void debug(Object message, Throwable t) {
109                    throw new UnsupportedOperationException();
110                }
111    
112                public void info(Object message) {
113                    throw new UnsupportedOperationException();
114                }
115    
116                public void info(Object message, Throwable t) {
117                    throw new UnsupportedOperationException();
118                }
119    
120                public void warn(Object message) {
121                    throw new UnsupportedOperationException();
122                }
123    
124                public void warn(Object message, Throwable t) {
125                    throw new UnsupportedOperationException();
126                }
127    
128                public void error(Object message) {
129                    error(message, null);
130                }
131    
132                public void error(Object message, Throwable t) {
133                    System.err.println(message);
134                    if (t != null) {
135                        System.err.println(t.getMessage());
136                        t.printStackTrace(System.err);
137                    }
138                }
139    
140                public void fatal(Object message) {
141                    throw new UnsupportedOperationException();
142                }
143    
144                public void fatal(Object message, Throwable t) {
145                    throw new UnsupportedOperationException();
146                }
147    
148            });
149        }
150    
151    
152    
153    
154        /**
155         * A callback storing indexes.
156         *
157         * <p>Note that errors are not stored or reported.</p>
158         */
159        public static class IndexToNameStoringCallback implements MoleculeCallback {
160    
161            /**
162             * Callback to also forward; null if no forward required.
163             */
164            private final MoleculeCallback with;
165    
166    
167            /**
168             * List storing molecule names for each index.
169             */
170            private final List<String> indexToName;
171    
172            /**
173             * Construct.
174             *
175             * @param with  Callback to also invoke
176             */
177            public IndexToNameStoringCallback(MoleculeCallback with) {
178                this.with = with;
179                this.indexToName = new ArrayList<String>();
180            }
181    
182            /**
183             * Construct.
184             */
185            public IndexToNameStoringCallback() {
186                this.with = null;
187                this.indexToName = new ArrayList<String>();
188            }
189    
190            @Override
191            public void notifyMolecule(int readno, Optional<String> molString, Molecule m, int index) {
192                if (this.with != null) {
193                    this.with.notifyMolecule(readno, molString, m, index);
194                }
195                if (index != this.indexToName.size()) {
196                    throw new IllegalStateException("Expected index " + this.indexToName.size() + "; got " + index
197                            + " (for readno: " + readno + ")");
198                }
199                this.indexToName.add(m.getName());
200            }
201    
202            @Override
203            public void notifyProcessingError(int readno, Optional<String> molString, Molecule m, Throwable t) {
204                if (this.with != null) {
205                    this.with.notifyProcessingError(readno, molString, m, t);
206                }
207            }
208    
209            @Override
210            public void notifyParseError(int readno, Optional<String> molString, Throwable t) {
211                if (this.with != null) {
212                    this.with.notifyParseError(readno, molString, t);
213                }
214            }
215    
216            /**
217             * Get a clone of index to name association.
218             *
219             * <p>Note that an {@link ImmutableList} is constructed on every invocation which is not cached.</p>
220             *
221             * @return  Index to name asspcoayopn
222             */
223            public ImmutableList<String> getIndexToName() {
224                return ImmutableList.copyOf(this.indexToName);
225            }
226    
227            /**
228             * Print index to name associations into escaped strings.
229             *
230             * <p>Each line will be scaped using {@link StringEscapeUtils#escapeJava(java.lang.String)}.</p>
231             *
232             * @param ps    Stream to write; will not be closed
233             * @param po    Progress Observer to track progress; will be closed
234             * @throws CancellationException when cancelled
235             */
236            public void toStrings(PrintStream ps, SubProgressObserver po) {
237                po.switchToDeterminate(this.indexToName.size());
238                try {
239                    for (String s : this.indexToName) {
240                        ps.println(StringEscapeUtils.escapeJava(s));
241                        po.worked(1);
242                    }
243                } finally {
244                    po.done();
245                }
246            }
247    
248            @Override
249            public String toString() {
250                final String s = "Index to name size: " + this.indexToName.size();
251                if (this.with == null) {
252                    return s;
253                } else {
254                    return s + " (with " + this.with.toString() + ")";
255                }
256            }
257    
258        }
259    
260    
261        /**
262         * Error counting callback.
263         */
264        public interface CountingMoleculeCallback extends MoleculeCallback {
265    
266            /**
267             * Count of all notify calls.
268             * @return  All notify calls
269             */
270            int getTotalNotifyCount();
271    
272            /**
273             * Count of OKs.
274             * @return  OK call count.
275             */
276            int getOkCount();
277    
278            /**
279             * Processing error count.
280             * @return  Proessing error count.
281             */
282            int getProcessingErrorCount();
283    
284            /**
285             * Parse error count.
286             * @return  Parse errors.
287             */
288            int getParseErrorCount();
289        }
290    
291        /**
292         * Null callback, does nothing.
293         */
294        private static class NullMoleculeCallback implements MoleculeCallback {
295    
296            @Override
297            public void notifyMolecule(int readno, Optional<String> molString, Molecule m, int index) {}
298    
299            @Override
300            public void notifyProcessingError(int readno, Optional<String> molString, Molecule m, Throwable t) {}
301    
302            @Override
303            public void notifyParseError(int readno, Optional<String> molString, Throwable t) { }
304    
305        }
306    
307    
308        /**
309         * Counting callback implementation.
310         */
311        private static class CountingMoleculeCallbackImpl implements CountingMoleculeCallback {
312    
313            /**
314             * Logger to log errors or null.
315             */
316            private final Log log;
317    
318            /**
319             * Ok count.
320             */
321            private int ok = 0;
322    
323            /**
324             * Processing error count.
325             */
326            private int proce = 0;
327    
328            /**
329             * Parse error coumt.
330             */
331            private int parsee = 0;
332    
333            /**
334             * Construct with no logging.
335             */
336            public CountingMoleculeCallbackImpl() {
337                this.log = null;
338            }
339    
340            /**
341             * Construct with logging.
342             *
343             * @param log   Logger to log errors.
344             */
345            public CountingMoleculeCallbackImpl(Log log) {
346                this.log = log;
347            }
348    
349            @Override
350            public int getTotalNotifyCount() {
351                return this.ok + this.proce + this.parsee;
352            }
353    
354            @Override
355            public int getOkCount() {
356                return this.ok;
357            }
358    
359            @Override
360            public int getProcessingErrorCount() {
361                return this.proce;
362            }
363    
364            @Override
365            public int getParseErrorCount() {
366                return this.parsee;
367            }
368    
369            @Override
370            public void notifyMolecule(int readno, Optional<String> molString, Molecule m, int index) {
371                //System.err.println("Mol src: " + molString.get());
372                // System.err.println("Mol name: " + m.getName());
373                this.ok++;
374            }
375    
376            @Override
377            public void notifyProcessingError(int readno, Optional<String> molString, Molecule m, Throwable t) {
378                this.proce++;
379                if (this.log != null && this.log.isErrorEnabled()) {
380                    this.log.error("-------------------------------------------------------------------------------------");
381                    this.log.error("Process error");
382                    this.log.error("  readno:    " + readno);
383                    this.log.error("  molString: " + molString.toString());
384                    this.log.error("  Error:     "  + t.getMessage(), t);
385                    this.log.error("-------------------------------------------------------------------------------------");
386                    this.log.error("");
387                }
388            }
389    
390            @Override
391            public void notifyParseError(int readno, Optional<String> molString, Throwable t) {
392                this.parsee++;
393                if (this.log != null && this.log.isErrorEnabled()) {
394                    this.log.error("-------------------------------------------------------------------------------------");
395                    this.log.error("Parse error");
396                    this.log.error("  readno:    " + readno);
397                    this.log.error("  molString: " + molString.toString());
398                    this.log.error("  Error:     "  + t.getMessage(), t);
399                    this.log.error("-------------------------------------------------------------------------------------");
400                    this.log.error("");
401                }
402            }
403    
404            @Override
405            public String toString() {
406                return "Total: " + getTotalNotifyCount()
407                        + " OK: " + getOkCount()
408                        + " Parse error: " + getParseErrorCount()
409                        + " Process error: " + getProcessingErrorCount();
410            }
411    
412    
413    
414        }
415    
416    }