package org.docear.plugin.core; import java.awt.Color; import java.awt.Font; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import org.docear.plugin.core.event.DocearEvent; import org.docear.plugin.core.event.DocearEventQueue; import org.docear.plugin.core.event.DocearEventType; import org.docear.plugin.core.event.IDocearEventListener; import org.docear.plugin.core.features.DocearLifeCycleObserver; import org.docear.plugin.core.features.DocearMapModelExtension; import org.docear.plugin.core.features.DocearProgressObserver; import org.docear.plugin.core.io.IOTools; import org.docear.plugin.core.listeners.MapWithoutProjectHandler; import org.docear.plugin.core.logger.DocearEventLogger; import org.docear.plugin.core.logging.DocearLogger; import org.docear.plugin.core.ui.wizard.Wizard; import org.docear.plugin.core.workspace.model.DocearWorkspaceProject; import org.freeplane.core.resources.ResourceController; import org.freeplane.core.util.Compat; import org.freeplane.core.util.LogUtils; import org.freeplane.features.map.MapModel; import org.freeplane.features.mode.Controller; import org.freeplane.plugin.workspace.URIUtils; import org.freeplane.plugin.workspace.WorkspaceController; import org.freeplane.plugin.workspace.features.WorkspaceMapModelExtension; import org.freeplane.plugin.workspace.model.WorkspaceModel; import org.freeplane.plugin.workspace.model.project.AWorkspaceProject; /** * */ public class DocearController implements IDocearEventListener { static final String DOCEAR_FIRST_RUN_PROPERTY = "docear.already_initialized"; static final String DOCEAR_SERVICE_NOT_AVAILABLE_PROPERTY = "docear.service_not_available"; private final static String DOCEAR_VERSION_NUMBER = "docear.version.number"; private String applicationName; private String applicationVersion; private String applicationStatus; private String applicationStatusVersion; private int applicationBuildNumber; private String applicationBuildDate; private final SemaphoreController semaphoreController = new SemaphoreController(); private final DocearEventLogger docearEventLogger = new DocearEventLogger(); private final static DocearController docearController = new DocearController(); private final DocearEventQueue eventQueue = new DocearEventQueue(); private final Set<String> workingThreads = new HashSet<String>(); private final boolean firstRun; private boolean applicationShutdownAborted = false; private Map<Class<?>, Set<DocearProgressObserver>> progressObservers = new TreeMap<Class<?>, Set<DocearProgressObserver>>(new Comparator<Class<?>>() { public int compare(Class<?> c1, Class<?> c2) { if(c1.equals(c2)) { return 0; } return c1.getName().compareTo(c2.getName()); } }); private DocearLifeCycleObserver lifeCycleObserver; /*********************************************************************************** * CONSTRUCTORS **********************************************************************************/ protected DocearController() { firstRun = !DocearController.getPropertiesController().getBooleanProperty(DOCEAR_FIRST_RUN_PROPERTY); checkServiceAvailability(); setApplicationIdentifiers(); eventQueue.addEventListener(this); } /*********************************************************************************** * METHODS **********************************************************************************/ public boolean isDocearFirstStart() { return firstRun; } public DocearEventQueue getEventQueue() { return eventQueue; } public boolean hasOutdatedConfigFiles() { try { File file = new File(Compat.getApplicationUserDirectory()); for (File dir : file.listFiles()) { if (dir.getName().equals("ribbons")) { return false; } } } catch (Exception e) { DocearLogger.warn(e); } return true; } public boolean isLicenseDialogNecessary() { int storedBuildNumber = Integer.parseInt(DocearController.getPropertiesController().getProperty(DOCEAR_VERSION_NUMBER, "0")); if (storedBuildNumber == 0 || hasOutdatedConfigFiles()) { DocearController.getPropertiesController().setProperty(DOCEAR_VERSION_NUMBER, ""+this.applicationBuildNumber); return true; } else { return false; } } public static DocearController getController() { return docearController; } public void addWorkingThreadHandle(String handleId) { if(handleId == null) { return; } synchronized (workingThreads) { workingThreads.add(handleId); } } public void removeWorkingThreadHandle(String handleId) { if(handleId == null) { return; } synchronized (workingThreads) { workingThreads.remove(handleId); } } public void setApplicationIdentifiers() { final Properties versionProperties = new Properties(); InputStream in = null; try { in = this.getClass().getResource("/version.properties").openStream(); versionProperties.load(in); } catch (final IOException e) { } final Properties buildProperties = new Properties(); in = null; try { in = this.getClass().getResource("/build.number").openStream(); buildProperties.load(in); } catch (final IOException e) { } setApplicationName("Docear"); setApplicationVersion(versionProperties.getProperty("docear_version")); setApplicationStatus(versionProperties.getProperty("docear_version_status")); setApplicationStatusVersion(versionProperties.getProperty("docear_version_status_number")); setApplicationBuildDate(versionProperties.getProperty("build_date")); setApplicationBuildNumber(Integer.parseInt(buildProperties.getProperty("build.number")) -1); } public Version getVersion() { try { DocearController docearController = DocearController.getController(); Version version = new Version(); String[] versionStrings = docearController.getApplicationVersion().split("\\."); version.setMajorVersion(Integer.parseInt(versionStrings[0])); version.setMiddleVersion(Integer.parseInt(versionStrings[1])); version.setMinorVersion(Integer.parseInt(versionStrings[2])); version.setStatus(docearController.getApplicationStatus()); version.setStatusNumber(Integer.parseInt(docearController.getApplicationStatusVersion())); version.setBuildNumber(docearController.getApplicationBuildNumber()); return version; } catch(Exception e) { LogUtils.warn(e); return null; } } public DocearEventLogger getDocearEventLogger() { return this.docearEventLogger; } public String getApplicationName() { return this.applicationName; } public void setApplicationName(String applicationName) { this.applicationName = applicationName; } public String getApplicationVersion() { return applicationVersion; } private void setApplicationVersion(String applicationVersion) { this.applicationVersion = applicationVersion; } public String getApplicationStatus() { return applicationStatus; } private void setApplicationStatus(String applicationStatus) { this.applicationStatus = applicationStatus; } public String getApplicationStatusVersion() { return applicationStatusVersion; } private void setApplicationStatusVersion(String applicationStatusVersion) { this.applicationStatusVersion = applicationStatusVersion; } public int getApplicationBuildNumber() { return applicationBuildNumber; } private void setApplicationBuildNumber(int i) { this.applicationBuildNumber = i; } public String getApplicationBuildDate() { return applicationBuildDate; } private void setApplicationBuildDate(String applicationBuildDate) { this.applicationBuildDate = applicationBuildDate; } private boolean hasWorkingThreads() { synchronized (workingThreads) { return !workingThreads.isEmpty(); } } public boolean shutdown() { getEventQueue().dispatchEvent(new DocearEvent(this, null, DocearEventType.APPLICATION_CLOSING)); Controller.getCurrentController().getViewController().saveProperties(); DocearController.getPropertiesController().saveProperties(); ResourceController.getResourceController().saveProperties(); if(!waitThreadsReady()){ return false; } if(Controller.getCurrentController().getViewController().quit()) { getEventQueue().dispatchEvent(new DocearEvent(this, null, DocearEventType.FINISH_THREADS)); if(!waitThreadsReady()){ return false; } } else { return false; } return true; } public void addProgressObserver(Class<?> clazz, DocearProgressObserver observer) { Set<DocearProgressObserver> observers = progressObservers.get(clazz); if(observers == null) { observers = new HashSet<DocearProgressObserver>(); progressObservers.put(clazz, observers); } observers.add(observer); } public Collection<DocearProgressObserver> getProgressObservers(Class<?> clazz) { Collection<DocearProgressObserver> observers = progressObservers.get(clazz); if(observers == null) { observers = Collections.emptySet(); } return observers; } private boolean waitThreadsReady() { while(hasWorkingThreads()) { try { Thread.sleep(100); } catch (InterruptedException e1) { } } if(this.applicationShutdownAborted){ this.applicationShutdownAborted = false; return false; } return true; } public String getGPLv2Terms() { try { return IOTools.getStringFromStream(DocearController.class.getResourceAsStream("/gplv2.html"),"UTF-8"); } catch (IOException e) { LogUtils.warn(e); return "GPLv2"; } } public String getDataProcessingTerms() { try { return IOTools.getStringFromStream(DocearController.class.getResourceAsStream("/Docear_data_processing.txt"),"UTF-8"); } catch (IOException e) { LogUtils.warn(e); return "Data Processing"; } } public String getDataPrivacyTerms() { try { return IOTools.getStringFromStream(DocearController.class.getResourceAsStream("/Docear_data_privacy.txt"), "UTF-8"); } catch (IOException e) { LogUtils.warn(e); return "Data Privacy"; } } public String getTermsOfService() { try { return IOTools.getStringFromStream(DocearController.class.getResourceAsStream("/Docear_terms_of_use.txt"),"UTF-8"); } catch (IOException e) { LogUtils.warn(e); return "Terms of Use"; } } protected void setLifeCycleObserver(DocearLifeCycleObserver observer) { if(lifeCycleObserver != null) { throw new RuntimeException("observer already set"); } this.lifeCycleObserver = observer; } public DocearLifeCycleObserver getLifeCycleObserver() { return this.lifeCycleObserver; } public static ResourceController getPropertiesController() { return ResourceController.getResourceController(); } public static AWorkspaceProject findProject(MapModel map) { AWorkspaceProject project = null; WorkspaceMapModelExtension mapExt = WorkspaceController.getMapModelExtension(map, false); if(mapExt != null) { project = mapExt.getProject(); } if(project == null) { WorkspaceModel model = WorkspaceController.getCurrentModel(); for (AWorkspaceProject prj : model.getProjects()) { URI relativeUri = prj.getRelativeURI(map.getFile().toURI()); //DOCEAR - validate: check whether a path is within the projects home if(relativeUri != null && !relativeUri.getRawPath().startsWith("/..") && !"file".equals(relativeUri.getScheme())) { project = prj; WorkspaceMapModelExtension ext = WorkspaceController.getMapModelExtension(map); ext.setProject(project); break; } } } if (project == null) { project = MapWithoutProjectHandler.showProjectSelectionWizard(map, false); } return project; } //TODO Service public boolean isServiceAvailable(){ return !DocearController.getPropertiesController().getBooleanProperty(DOCEAR_SERVICE_NOT_AVAILABLE_PROPERTY); } //TODO Service private void checkServiceAvailability(){ Map<String, String> serviceProperties = getServiceProperties(); if(serviceProperties.get("webservice_status") == null) return; if(serviceProperties.get("webservice_status").equalsIgnoreCase("0")){ if(isServiceAvailable()){ DocearController.getPropertiesController().setProperty(DOCEAR_SERVICE_NOT_AVAILABLE_PROPERTY, true); if(!isDocearFirstStart()){ showServiceMessage(serviceProperties.get("webservice_message"), serviceProperties.get("webservice_details_url")); } } } if(serviceProperties.get("webservice_status").equalsIgnoreCase("1")){ if(!isServiceAvailable()){ DocearController.getPropertiesController().setProperty(DOCEAR_SERVICE_NOT_AVAILABLE_PROPERTY, false); if(!isDocearFirstStart()){ showServiceMessage(serviceProperties.get("webservice_message"), serviceProperties.get("webservice_details_url")); } } } } //TODO Service private void showServiceMessage(String message, String detailURL){ JLabel label = new JLabel(); Font font = label.getFont(); StringBuffer style = new StringBuffer("font-family:" + font.getFamily() + ";"); style.append("font-weight:" + (font.isBold() ? "bold" : "normal") + ";"); style.append("font-size:" + font.getSize() + "pt;"); JEditorPane ep = new JEditorPane("text/html", "<html><body style=\"" + style + "\" >" // + message +"<br><br>For further detail follow this link: <a href=\""+detailURL+"\">"+detailURL+"</a>" // + "</body></html>"); // handle link events ep.addHyperlinkListener(new HyperlinkListener() { @Override public void hyperlinkUpdate(HyperlinkEvent evt) { if (evt.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) try { Controller.getCurrentController().getViewController().openDocument(evt.getURL()); } catch (Exception e) { LogUtils.warn(e); } } }); ep.setEditable(false); ep.setBackground(new Color(214,217,223,0)); JOptionPane.showMessageDialog(null, ep); } //TODO Service private Map<String, String> getServiceProperties() { URI statusURI = URI.create("http://www.docear.org/services/status.php"); Map<String, String> properties = new HashMap<String, String>(); boolean webService = false; boolean version = false; HttpURLConnection statusConnection = null; try { statusConnection = (HttpURLConnection)statusURI.toURL().openConnection(); statusConnection.setRequestMethod("GET"); statusConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11"); statusConnection.connect(); BufferedReader in = new BufferedReader(new InputStreamReader(statusConnection.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null){ if(inputLine.contains("[Web services]")){ webService = true; version = false; continue; } if(inputLine.contains("[Version]")){ webService = false; version = true; continue; } inputLine = inputLine.replaceAll("\t/.*", ""); inputLine = inputLine.replaceAll("\"", ""); inputLine = inputLine.replaceAll("\t", ""); String[] keyValuePair = inputLine.split("="); if(keyValuePair.length == 2){ if(webService){ properties.put("webservice_" + keyValuePair[0].trim(), keyValuePair[1].trim()); } if(version){ properties.put("version_" + keyValuePair[0].trim(), keyValuePair[1].trim()); } } } in.close(); } catch (MalformedURLException e) { LogUtils.warn(e); } catch (IOException e) { LogUtils.warn(e); } finally{ if(statusConnection != null){ statusConnection.disconnect(); } } return properties; } /*********************************************************************************** * REQUIRED METHODS FOR INTERFACES **********************************************************************************/ public void handleEvent(DocearEvent event) { if(event.getType() == DocearEventType.APPLICATION_CLOSING_ABORTED){ this.applicationShutdownAborted = true; } } public SemaphoreController getSemaphoreController() { return semaphoreController; } public boolean isLibraryMap(MapModel map) { if(map == null) { return false; } DocearMapModelExtension dmme = map.getExtension(DocearMapModelExtension.class); if (dmme == null) { return false; } AWorkspaceProject project = WorkspaceController.getMapProject(map); if(DocearWorkspaceProject.isCompatible(project)) { ((DocearWorkspaceProject)project).getLibraryMaps(); for (URI uri : ((DocearWorkspaceProject)project).getLibraryMaps()) { if (uri != null) { String path = map.getFile().getAbsolutePath(); File f = URIUtils.getAbsoluteFile(uri); if (f != null && f.getAbsolutePath().equals(path)) { return true; } } } } return false; } }