package org.docear.plugin.pdfutilities.features; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.docear.pdf.annotation.AnnotationExtractor; import org.docear.pdf.bookmark.BookmarkExtractor; import org.docear.pdf.feature.ADocumentCreator; import org.docear.pdf.feature.APDMetaObject; import org.docear.plugin.core.features.DocearMapModelController; import org.docear.plugin.pdfutilities.features.IAnnotation.AnnotationType; import org.docear.plugin.pdfutilities.map.AnnotationController; import org.freeplane.core.util.LogUtils; import org.freeplane.features.map.MapModel; import org.freeplane.features.map.NodeModel; import org.freeplane.plugin.workspace.URIUtils; import de.intarsys.pdf.pd.PDDocument; public class SingleMapConversionHandler implements IConversionProcessHandler { private final Map<File, ExtractorAdaptor> documentCache = new HashMap<File, ExtractorAdaptor>(); private int convertCount = 0; /*********************************************************************************** * CONSTRUCTORS **********************************************************************************/ /*********************************************************************************** * METHODS **********************************************************************************/ public void convert(MapModel map) { long time = System.currentTimeMillis(); try { convertCount = 0; convertAnnotation(map.getRootNode()); DocearMapModelController.getModel(map).setVersion(DocearMapModelController.CURRENT_MAP_VERSION); map.setSaved(false); } finally { LogUtils.info("\n\n" + map.getFile() + "\n\n" + (System.currentTimeMillis() - time) + "\n\n(nodeCount: "+convertCount+")\n\n"); close(); System.gc(); } } private void convertAnnotation(NodeModel node) { AnnotationModel extensionModel = AnnotationController.getModel(node, false); if (extensionModel != null) { convertCount++; if(extensionModel.getOldObjectNumber() > 0) { try { File file = URIUtils.getFile(extensionModel.getSource()); synchronized (documentCache) { ExtractorAdaptor adapter = getCachedExtractor(file); if (adapter != null) { APDMetaObject annotation = adapter.findMetaForObjectNumber(extensionModel.getOldObjectNumber()); if (annotation != null) { AnnotationController.setModel(node, AnnotationConverter.cloneAnnotation(annotation, extensionModel)); } } } } catch (Exception e) { LogUtils.warn(e); } } else { if(AnnotationType.PDF_FILE.equals(extensionModel.getAnnotationType())) { AnnotationController.setModel(node, AnnotationConverter.cloneAnnotation(0, extensionModel)); } } } for (NodeModel child : node.getChildren()) { convertAnnotation(child); } } private ExtractorAdaptor getCachedExtractor(File file) throws IOException { ExtractorAdaptor adapter = documentCache.get(file); if (adapter == null) { if(file.exists()) { trimCache(); adapter = new ExtractorAdaptor(file); documentCache.put(file, adapter); } } return adapter; } private void trimCache() { if (documentCache.size() % 200 == 0) { System.gc(); } } public void close() { synchronized (documentCache) { for (Entry<File, ExtractorAdaptor> entry : documentCache.entrySet()) { try { entry.getValue().close(); } catch (IOException e) { LogUtils.warn(e); } } documentCache.clear(); } } @Override protected void finalize() throws Throwable { super.finalize(); close(); System.gc(); } /*********************************************************************************** * REQUIRED METHODS FOR INTERFACES **********************************************************************************/ public static class ExtractorAdaptor { private Map<Integer, APDMetaObject> objNumberIndex; /*********************************************************************************** * CONSTRUCTORS * @throws IOException **********************************************************************************/ public ExtractorAdaptor(File file) throws IOException { if(file == null) { throw new IllegalArgumentException("NULL"); } initializeIndex(file); } /*********************************************************************************** * METHODS **********************************************************************************/ private void indexMetaList(List<APDMetaObject> metas) { for (APDMetaObject meta : metas) { if(meta.getObjectNumber() > 0) { objNumberIndex.put(meta.getObjectNumber(), meta); } if(meta.hasChildren()) { indexMetaList(meta.getChildren()); } } } private void initializeIndex(File file) throws IOException { objNumberIndex = new LinkedHashMap<Integer, APDMetaObject>(); PDDocument document = ADocumentCreator.getPDDocument(file); BookmarkExtractor bookmarkExtractor = null; try { bookmarkExtractor = new BookmarkExtractor(document); indexMetaList(bookmarkExtractor.getMetaObjects()); } catch (IOException e) { LogUtils.warn("Exception in org.docear.plugin.pdfutilities.features.SingleMapConversionHandler.ExtractorAdaptor.initializeIndex("+file+")...bookmarks: "+e.getMessage()); } AnnotationExtractor annotationExtractor = null; try { annotationExtractor = new AnnotationExtractor(document); indexMetaList(annotationExtractor.getMetaObjects()); } catch (IOException e) { LogUtils.warn("Exception in org.docear.plugin.pdfutilities.features.SingleMapConversionHandler.ExtractorAdaptor.initializeIndex("+file+")...annotations: "+e.getMessage()); } try { if(!document.isReadOnly()) { if((bookmarkExtractor != null && bookmarkExtractor.isDocumentModified()) || (annotationExtractor != null && annotationExtractor.isDocumentModified())) { document.save(); } } } catch (IOException e) { LogUtils.warn("Exception in org.docear.plugin.pdfutilities.features.SingleMapConversionHandler.ExtractorAdaptor.initializeIndex("+file+")...save: "+e.getMessage()); } //close and clear all temporarily objects try { if(annotationExtractor != null) { annotationExtractor.resetAll(); } if(bookmarkExtractor != null) { bookmarkExtractor.resetAll(); } if(document != null) { document.close(); } } catch (IOException e) { LogUtils.warn("Exception in org.docear.plugin.pdfutilities.features.SingleMapConversionHandler.ExtractorAdaptor.initializeIndex("+file+")...close: "+e.getMessage()); } } public APDMetaObject findMetaForObjectNumber(Integer objectNumber) { return objNumberIndex.get(objectNumber); } public void close() throws IOException { objNumberIndex.clear(); } /*********************************************************************************** * REQUIRED METHODS FOR INTERFACES **********************************************************************************/ } }