Package org.rssowl.ui.internal.dialogs.importer

Source Code of org.rssowl.ui.internal.dialogs.importer.ImportElementsPage$ExistingBookmarkFilter

/*   **********************************************************************  **
**   Copyright notice                                                       **
**                                                                          **
**   (c) 2005-2009 RSSOwl Development Team                                  **
**   http://www.rssowl.org/                                                 **
**                                                                          **
**   All rights reserved                                                    **
**                                                                          **
**   This program and the accompanying materials are made available under   **
**   the terms of the Eclipse Public License v1.0 which accompanies this    **
**   distribution, and is available at:                                     **
**   http://www.rssowl.org/legal/epl-v10.html                               **
**                                                                          **
**   A copy is found in the file epl-v10.html and important notices to the  **
**   license from the team is found in the textfile LICENSE.txt distributed **
**   in this package.                                                       **
**                                                                          **
**   This copyright notice MUST APPEAR in all copies of the file!           **
**                                                                          **
**   Contributors:                                                          **
**     RSSOwl Development Team - initial API and implementation             **
**                                                                          **
**  **********************************************************************  */

package org.rssowl.ui.internal.dialogs.importer;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.rssowl.core.Owl;
import org.rssowl.core.connection.AuthenticationRequiredException;
import org.rssowl.core.connection.ConnectionException;
import org.rssowl.core.connection.CredentialsException;
import org.rssowl.core.connection.HttpConnectionInputStream;
import org.rssowl.core.connection.IAbortable;
import org.rssowl.core.connection.IConnectionPropertyConstants;
import org.rssowl.core.connection.ICredentials;
import org.rssowl.core.connection.IProtocolHandler;
import org.rssowl.core.interpreter.ITypeImporter;
import org.rssowl.core.interpreter.InterpreterException;
import org.rssowl.core.interpreter.ParserException;
import org.rssowl.core.persist.IBookMark;
import org.rssowl.core.persist.IEntity;
import org.rssowl.core.persist.IFeed;
import org.rssowl.core.persist.IFolder;
import org.rssowl.core.persist.IFolderChild;
import org.rssowl.core.persist.ILabel;
import org.rssowl.core.persist.INewsBin;
import org.rssowl.core.persist.IPreference;
import org.rssowl.core.persist.ISearchFilter;
import org.rssowl.core.persist.ISearchMark;
import org.rssowl.core.persist.dao.DynamicDAO;
import org.rssowl.core.persist.dao.IBookMarkDAO;
import org.rssowl.core.persist.reference.FeedLinkReference;
import org.rssowl.core.util.CoreUtils;
import org.rssowl.core.util.Pair;
import org.rssowl.core.util.RegExUtils;
import org.rssowl.core.util.StringUtils;
import org.rssowl.core.util.SyncUtils;
import org.rssowl.core.util.URIUtils;
import org.rssowl.ui.internal.Activator;
import org.rssowl.ui.internal.Application;
import org.rssowl.ui.internal.Controller;
import org.rssowl.ui.internal.OwlUI;
import org.rssowl.ui.internal.dialogs.CustomWizardDialog;
import org.rssowl.ui.internal.dialogs.LoginDialog;
import org.rssowl.ui.internal.dialogs.PreviewFeedDialog;
import org.rssowl.ui.internal.dialogs.importer.ImportSourcePage.Source;
import org.rssowl.ui.internal.dialogs.welcome.WelcomeWizard;
import org.rssowl.ui.internal.util.FolderChildCheckboxTree;
import org.rssowl.ui.internal.util.JobRunner;
import org.rssowl.ui.internal.util.LayoutUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
*A {@link WizardPage} to select the elements to import.
*
* @author bpasero
*/
public class ImportElementsPage extends WizardPage {

  /* Initial Connection Timeout when looking for Feeds remotely */
  private static final int INITIAL_CON_TIMEOUT = 30000;

  /* Connection Timeout when testing for Feeds remotely */
  private static final int FEED_CON_TIMEOUT = 7000;

  /* Default Feed Search Language */
  private static final String DEFAULT_LANGUAGE = "en"; //$NON-NLS-1$

  /* URL to export from Google Reader */
  private static final String GOOGLE_READER_OPML_URI = "https://www.google.com/reader/subscriptions/export"; //$NON-NLS-1$

  private CheckboxTreeViewer fViewer;
  private FolderChildCheckboxTree fFolderChildTree;
  private Button fDeselectAll;
  private Button fSelectAll;
  private Button fPreviewButton;
  private Button fFlattenCheck;
  private Button fHideExistingCheck;
  private ExistingBookmarkFilter fExistingFilter = new ExistingBookmarkFilter();
  private Map<URI, IFeed> fLoadedFeedCache = new ConcurrentHashMap<URI, IFeed>();
  private IProgressMonitor fCurrentProgressMonitor;

  /* Remember Current Import Values */
  private Source fCurrentSourceKind;
  private String fCurrentSourceResource;
  private String fCurrentSourceKeywords;
  private boolean fCurrentSourceLocalizedFeedSearch;
  private long fCurrentSourceFileModified;

  /* Imported Entities */
  private List<ILabel> fLabels = Collections.synchronizedList(new ArrayList<ILabel>());
  private List<ISearchFilter> fFilters = Collections.synchronizedList(new ArrayList<ISearchFilter>());
  private List<IPreference> fPreferences = Collections.synchronizedList(new ArrayList<IPreference>());

  /* Filter to Exclude Existing Bookmarks (empty folders are excluded as well) */
  private static class ExistingBookmarkFilter extends ViewerFilter {
    private IBookMarkDAO dao = DynamicDAO.getDAO(IBookMarkDAO.class);
    private Map<IFolderChild, Boolean> cache = new IdentityHashMap<IFolderChild, Boolean>();

    @Override
    public boolean select(Viewer viewer, Object parentElement, Object element) {
      if (element instanceof IFolderChild)
        return select((IFolderChild) element);

      return true;
    }

    void clear() {
      cache.clear();
    }

    private boolean select(IFolderChild element) {

      /* Bookmark (exclude if another Bookmark with same Link exists) */
      if (element instanceof IBookMark) {
        IBookMark bm = (IBookMark) element;
        Boolean select = cache.get(bm);
        if (select == null) {
          select = !dao.exists(bm.getFeedLinkReference());
          cache.put(bm, select);
        }

        return select;
      }

      /* Bin (exclude if another Bin with same name Exists at same Location) */
      else if (element instanceof INewsBin) {
        INewsBin bin = (INewsBin) element;
        Boolean select = cache.get(bin);
        if (select == null) {
          select = !CoreUtils.existsNewsBin(bin);
          cache.put(bin, select);
        }

        return select;
      }

      /* Search (exclude if another Search with same name Exists at same Location and same Conditions) */
      else if (element instanceof ISearchMark) {
        ISearchMark searchmark = (ISearchMark) element;
        Boolean select = cache.get(searchmark);
        if (select == null) {
          select = !CoreUtils.existsSearchMark(searchmark);
          cache.put(searchmark, select);
        }

        return select;
      }

      /* Folder */
      else if (element instanceof IFolder) {
        IFolder folder = (IFolder) element;
        Boolean select = cache.get(folder);
        if (select == null) {
          List<IFolderChild> children = folder.getChildren();
          for (IFolderChild child : children) {
            select = select(child);
            if (select)
              break;
          }

          cache.put(folder, select);
        }

        return select != null ? select : false;
      }

      return true;
    }
  }

  ImportElementsPage() {
    super(Messages.ImportElementsPage_CHOOSE_ELEMENTS, Messages.ImportElementsPage_CHOOSE_ELEMENTS, null);
    setMessage(Messages.ImportElementsPage_CHOOSE_ELEMENTS_MESSAGE);
  }

  /* Get Elements to Import */
  List<IFolderChild> getFolderChildsToImport() {
    doImportSource(); //Ensure to be in sync with Source
    return fFolderChildTree.getCheckedElements();
  }

  /* Returns Labels available for Import */
  List<ILabel> getLabelsToImport() {
    doImportSource(); //Ensure to be in sync with Source
    return fLabels;
  }

  /* Returns Filters available for Import */
  List<ISearchFilter> getFiltersToImport() {
    doImportSource(); //Ensure to be in sync with Source
    return fFilters;
  }

  /* Returns the Preferences available for Import */
  List<IPreference> getPreferencesToImport() {
    doImportSource(); //Ensure to be in sync with Source
    return fPreferences;
  }

  /* Returns whether existing bookmarks should be ignored for the Import */
  boolean excludeExisting() {
    return fHideExistingCheck.getSelection();
  }

  /* Check if the Options Page should be shown from the Wizard */
  boolean showOptionsPage() {
    return !fLabels.isEmpty() || !fFilters.isEmpty() || !fPreferences.isEmpty();
  }

  /*
   * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
   */
  public void createControl(Composite parent) {
    boolean isWelcome = (getWizard() instanceof WelcomeWizard);

    /* Title Image */
    setImageDescriptor(OwlUI.getImageDescriptor(getWizard() instanceof WelcomeWizard ? "icons/wizban/welcome_wiz.gif" : "icons/wizban/import_wiz.png")); //$NON-NLS-1$ //$NON-NLS-2$

    /* Container */
    Composite container = new Composite(parent, SWT.NONE);
    container.setLayout(new GridLayout(1, false));

    /* Viewer for Folder Child Selection */
    fFolderChildTree = new FolderChildCheckboxTree(container);
    if (!isWelcome)
      ((GridData) fFolderChildTree.getViewer().getTree().getLayoutData()).heightHint = 140;
    fViewer = fFolderChildTree.getViewer();

    /* Open Preview on Doubleclick */
    fViewer.addDoubleClickListener(new IDoubleClickListener() {
      public void doubleClick(DoubleClickEvent event) {
        openPreview(event.getSelection());
      }
    });

    /* Control Preview Button Enablement */
    fViewer.addSelectionChangedListener(new ISelectionChangedListener() {
      public void selectionChanged(SelectionChangedEvent event) {
        ISelection selection = event.getSelection();
        fPreviewButton.setEnabled(!selection.isEmpty() && ((IStructuredSelection) selection).getFirstElement() instanceof IBookMark);
      }
    });

    /* Filter (exclude existing) */
    fViewer.addFilter(fExistingFilter);

    /* Update Page Complete on Selection */
    fViewer.getTree().addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        updatePageComplete();
      }
    });

    /* Select All / Deselect All */
    Composite buttonContainer = new Composite(container, SWT.NONE);
    buttonContainer.setLayout(LayoutUtils.createGridLayout(isWelcome ? 5 : 6, 0, 0));
    buttonContainer.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));

    fSelectAll = new Button(buttonContainer, SWT.PUSH);
    fSelectAll.setText(Messages.ImportElementsPage_SELECT_ALL);
    Dialog.applyDialogFont(fSelectAll);
    setButtonLayoutData(fSelectAll);
    fSelectAll.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        fFolderChildTree.setAllChecked(true);
        updatePageComplete();
      }
    });

    fDeselectAll = new Button(buttonContainer, SWT.PUSH);
    fDeselectAll.setText(Messages.ImportElementsPage_DESELECT_ALL);
    Dialog.applyDialogFont(fDeselectAll);
    setButtonLayoutData(fDeselectAll);
    fDeselectAll.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        fFolderChildTree.setAllChecked(false);
        updatePageComplete();
      }
    });

    if (!Application.IS_MAC) {
      Label sep = new Label(buttonContainer, SWT.SEPARATOR | SWT.VERTICAL);
      sep.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, false, false));
      ((GridData) sep.getLayoutData()).heightHint = 20;
    }

    fPreviewButton = new Button(buttonContainer, SWT.PUSH);
    fPreviewButton.setText(Messages.ImportElementsPage_PREVIEW);
    fPreviewButton.setEnabled(false);
    fPreviewButton.setToolTipText(Messages.ImportElementsPage_SHOW_PREVIEW);
    Dialog.applyDialogFont(fPreviewButton);
    setButtonLayoutData(fPreviewButton);
    fPreviewButton.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        openPreview(fViewer.getSelection());
      }
    });

    /* Show as Flat List of News Marks */
    fFlattenCheck = new Button(buttonContainer, SWT.CHECK);
    fFlattenCheck.setText(Messages.ImportElementsPage_FLATTEN);
    Dialog.applyDialogFont(fFlattenCheck);
    setButtonLayoutData(fFlattenCheck);
    ((GridData) fFlattenCheck.getLayoutData()).horizontalAlignment = SWT.END;
    ((GridData) fFlattenCheck.getLayoutData()).horizontalIndent = 30;
    ((GridData) fFlattenCheck.getLayoutData()).grabExcessHorizontalSpace = true;
    fFlattenCheck.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        fFolderChildTree.setFlat(fFlattenCheck.getSelection());
        fViewer.expandToLevel(2);
        fFolderChildTree.setAllChecked(true);
      }
    });

    /* Hide Existing News Marks */
    fHideExistingCheck = new Button(buttonContainer, SWT.CHECK);
    fHideExistingCheck.setText(Messages.ImportElementsPage_HIDE_EXISTING);
    fHideExistingCheck.setSelection(true);
    Dialog.applyDialogFont(fHideExistingCheck);
    setButtonLayoutData(fHideExistingCheck);
    ((GridData) fHideExistingCheck.getLayoutData()).exclude = isWelcome;
    fHideExistingCheck.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        if (fHideExistingCheck.getSelection())
          fViewer.addFilter(fExistingFilter);
        else
          fViewer.removeFilter(fExistingFilter);

        fViewer.expandToLevel(2);
        updateMessage(false);
      }
    });

    /* React on user clicking Cancel if a progress is showing */
    if (getContainer() instanceof CustomWizardDialog) {
      Button cancelButton = ((CustomWizardDialog) getContainer()).getButton(IDialogConstants.CANCEL_ID);
      if (cancelButton != null) {
        cancelButton.addSelectionListener(new SelectionAdapter() {
          @Override
          public void widgetSelected(SelectionEvent e) {
            onCancel();
          }
        });
      }
    }

    Dialog.applyDialogFont(container);

    setControl(container);
  }

  private void onCancel() {
    if (fCurrentProgressMonitor != null && getShell() != null && !getShell().isDisposed()) {
      IProgressMonitor monitor = fCurrentProgressMonitor;
      monitor.setTaskName(Messages.ImportElementsPage_CANCEL_SEARCH);
      monitor.subTask(""); //$NON-NLS-1$
    }
  }

  private void openPreview(ISelection selection) {
    IStructuredSelection sel = (IStructuredSelection) selection;
    if (!sel.isEmpty()) {
      Object[] elements = sel.toArray();
      int offset = 0;
      for (Object element : elements) {
        if (element instanceof IBookMark) {
          IBookMark bookmark = (IBookMark) element;
          IFeed loadedFeed = fLoadedFeedCache.get(bookmark.getFeedLinkReference().getLink());

          PreviewFeedDialog dialog = new PreviewFeedDialog(getShell(), bookmark, loadedFeed);
          dialog.setBlockOnOpen(false);
          dialog.open();

          if (offset != 0) {
            Point location = dialog.getShell().getLocation();
            dialog.getShell().setLocation(location.x + offset, location.y + offset);
          }

          offset += 20;
        }
      }
    }
  }

  private void updateMessage(boolean clearErrors) {
    List<?> input = (List<?>) fViewer.getInput();
    if (!input.isEmpty() && fViewer.getTree().getItemCount() == 0 && fViewer.getFilters().length > 0)
      setMessage(Messages.ImportElementsPage_HIDDEN_ELEMENTS_INFO, IMessageProvider.WARNING);
    else
      setMessage(Messages.ImportElementsPage_CHOOSE_ELEMENTS_MESSAGE);

    if (clearErrors)
      setErrorMessage(null);

    updatePageComplete();
  }

  private void updatePageComplete() {
    boolean complete = (showOptionsPage() || fViewer.getCheckedElements().length > 0);
    setPageComplete(complete);
  }

  /*
   * @see org.eclipse.jface.dialogs.DialogPage#setVisible(boolean)
   */
  @Override
  public void setVisible(boolean visible) {
    super.setVisible(visible);
    if (!visible)
      return;

    fViewer.getControl().setFocus();

    /* Load Elements to Import from Source on first time */
    doImportSource();
  }

  @SuppressWarnings("unchecked")
  private void doImportSource() {
    ImportSourcePage importSourcePage = (ImportSourcePage) getPreviousPage();
    final Source source = importSourcePage.getSource();

    /* Return if the Source did not Change */
    if (source == Source.RECOMMENDED && fCurrentSourceKind == Source.RECOMMENDED)
      return;
    else if (source == Source.GOOGLE && fCurrentSourceKind == Source.GOOGLE)
      return;
    else if (source == Source.KEYWORD && fCurrentSourceKind == Source.KEYWORD && importSourcePage.getImportKeywords().equals(fCurrentSourceKeywords) && fCurrentSourceLocalizedFeedSearch == importSourcePage.isLocalizedFeedSearch())
      return;
    else if (source == Source.RESOURCE && fCurrentSourceKind == Source.RESOURCE) {
      String importResource = importSourcePage.getImportResource();

      /* Same URL */
      if (importSourcePage.isRemoteSource() && importResource.equals(fCurrentSourceResource))
        return;

      /* Same Unmodified File */
      else if (importResource.equals(fCurrentSourceResource)) {
        File file = new File(importResource);
        if (file.exists() && file.lastModified() == fCurrentSourceFileModified)
          return;
      }
    }

    /* Remember Source */
    fCurrentSourceKind = source;
    fCurrentSourceResource = importSourcePage.getImportResource();
    final File sourceFile = (source == Source.RESOURCE) ? new File(importSourcePage.getImportResource()) : null;
    fCurrentSourceFileModified = (sourceFile != null && sourceFile.exists()) ? sourceFile.lastModified() : 0;
    fCurrentSourceKeywords = importSourcePage.getImportKeywords();
    fCurrentSourceLocalizedFeedSearch = importSourcePage.isLocalizedFeedSearch();

    /* Reset Fields */
    fLabels.clear();
    fFilters.clear();
    fPreferences.clear();

    /* Reset Messages */
    setErrorMessage(null);
    setMessage(Messages.ImportElementsPage_CHOOSE_ELEMENTS_MESSAGE);

    /* Clear Viewer before loading */
    setImportedElements(Collections.EMPTY_LIST);

    /* Ask for Username and Password if importing from Google */
    if (source == Source.GOOGLE) {
      URI googleLoginUri = URI.create(SyncUtils.GOOGLE_LOGIN);
      LoginDialog dialog = new LoginDialog(getShell(), googleLoginUri, null);
      dialog.setHeader(Messages.ImportElementsPage_LOGIN_GOOGLE_READER);
      dialog.setSubline(Messages.ImportElementsPage_ENTER_GOOGLE_ACCOUNT);
      dialog.setTitleImageDescriptor(OwlUI.getImageDescriptor("icons/wizban/reader_wiz.png")); //$NON-NLS-1$
      if (dialog.open() != IDialogConstants.OK_ID) {
        setErrorMessage(Messages.ImportElementsPage_MISSING_ACCOUNT);
        setPageComplete(false);
        fCurrentSourceKind = null;
        return;
      }
    }

    /* Import Runnable */
    Runnable runnable = new Runnable() {
      public void run() {
        try {

          /* Import from Supplied File */
          if (source == Source.RESOURCE && sourceFile != null && sourceFile.exists())
            importFromLocalResource(sourceFile);

          /* Import from Supplied Online Resource */
          else if (source == Source.RESOURCE && URIUtils.looksLikeLink(fCurrentSourceResource))
            importFromOnlineResource(new URI(URIUtils.ensureProtocol(fCurrentSourceResource)));

          /* Import from Google */
          else if (source == Source.GOOGLE)
            importFromGoogleReader();

          /* Import by Keyword Search */
          else if (source == Source.KEYWORD)
            importFromKeywordSearch(fCurrentSourceKeywords, fCurrentSourceLocalizedFeedSearch);

          /* Import from Default OPML File */
          else if (source == Source.RECOMMENDED)
            importFromLocalResource(getClass().getResourceAsStream("/default_feeds.xml")); //$NON-NLS-1$;
        }

        /* Log and Show any Exception during Import */
        catch (Exception e) {

          /* Log Message */
          String logMessage = e.getMessage();
          if (e instanceof InvocationTargetException && e.getCause() != null && StringUtils.isSet(e.getCause().getMessage()))
            logMessage = e.getCause().getMessage();

          if (StringUtils.isSet(logMessage))
            logMessage = NLS.bind(Messages.ImportElementsPage_UNABLE_TO_IMPORT_REASON, logMessage);
          else
            logMessage = Messages.ImportElementsPage_UNABLE_TO_IMPORT;

          /* User Message */
          String userMessage = CoreUtils.toMessage(e);
          if (StringUtils.isSet(userMessage))
            userMessage = NLS.bind(Messages.ImportElementsPage_UNABLE_TO_IMPORT_REASON, userMessage);
          else
            userMessage = Messages.ImportElementsPage_UNABLE_TO_IMPORT;

          Activator.getDefault().logError(logMessage, e);
          setErrorMessage(userMessage);
          setPageComplete(false);

          /* Give a chance to try again */
          fCurrentSourceKind = null;
        }
      }
    };

    /* Perform delayed if potential remote import to give Viewer a chance to show */
    if (importSourcePage.isRemoteSource())
      JobRunner.runInUIThread(50, getShell(), runnable);
    else
      runnable.run();
  }

  /* Import from a Local Input Stream (no progress required) */
  private void importFromLocalResource(InputStream in) throws InterpreterException, ParserException {

    /* Show Folder Childs in Viewer */
    List<? extends IEntity> types = Owl.getInterpreter().importFrom(in);
    setImportedElements(types);
    updateMessage(true);
  }

  /* Import from a Local File */
  private void importFromLocalResource(final File file) throws FileNotFoundException, InvocationTargetException, InterruptedException {
    boolean bruteForce = false;

    /* First Try to Import as OPML */
    try {
      importFromLocalResource(new FileInputStream(file));
    } catch (ParserException e) {
      bruteForce = true;
    } catch (InterpreterException e) {
      bruteForce = true;
    }

    /* Then try to parse links found in the file as Feeds */
    if (bruteForce) {
      IRunnableWithProgress runnable = new IRunnableWithProgress() {
        public void run(final IProgressMonitor monitor) throws InvocationTargetException {
          monitor.beginTask(Messages.ImportElementsPage_SEARCHING_FOR_FEEDS, IProgressMonitor.UNKNOWN);
          fCurrentProgressMonitor = monitor;

          /* Return on Cancellation */
          if (monitor.isCanceled() || Controller.getDefault().isShuttingDown())
            return;

          /* Read Content */
          Reader reader = null;
          try {
            reader = new FileReader(file);
            String content = StringUtils.readString(reader);

            /* Extract Links from Content */
            List<String> links = new ArrayList<String>();
            if (StringUtils.isSet(content))
              links.addAll(RegExUtils.extractLinksFromText(content, false));

            /* Check Links for valid Feeds */
            importFromLinksBruteforce(links, monitor);
          } catch (Exception e) {
            throw new InvocationTargetException(e);
          } finally {
            fCurrentProgressMonitor = null;
            monitor.done();

            /* Close Reader */
            if (reader != null) {
              try {
                reader.close();
              } catch (IOException e) {
                throw new InvocationTargetException(e);
              }
            }
          }
        }
      };

      /* Run Operation in Background and allow for Cancellation */
      getContainer().run(true, true, runnable);
    }
  }

  private void importFromOnlineResource(final URI link) throws InvocationTargetException, InterruptedException {
    IRunnableWithProgress runnable = new IRunnableWithProgress() {
      public void run(final IProgressMonitor monitor) throws InvocationTargetException {
        InputStream in = null;
        boolean canceled = false;
        Exception error = null;
        boolean bruteForce = false;
        try {
          monitor.beginTask(Messages.ImportElementsPage_SEARCHING_FOR_FEEDS, IProgressMonitor.UNKNOWN);
          monitor.subTask(Messages.ImportElementsPage_CONNECTING);
          fCurrentProgressMonitor = monitor;

          /* Return on Cancellation */
          if (monitor.isCanceled() || Controller.getDefault().isShuttingDown()) {
            canceled = true;
            return;
          }

          /* Open Stream */
          in = openStream(link, monitor, INITIAL_CON_TIMEOUT, false, false, null);

          /* Return on Cancellation */
          if (monitor.isCanceled() || Controller.getDefault().isShuttingDown()) {
            canceled = true;
            return;
          }

          /* Try to Import */
          try {
            final List<? extends IEntity> types = Owl.getInterpreter().importFrom(in);

            /* Return on Cancellation */
            if (monitor.isCanceled() || Controller.getDefault().isShuttingDown()) {
              canceled = true;
              return;
            }

            /* Show in UI */
            JobRunner.runInUIThread(getShell(), new Runnable() {
              public void run() {
                setImportedElements(types);
                updateMessage(true);
              }
            });
          }

          /* Error Importing from File - Try Bruteforce then */
          catch (Exception e) {
            error = e;
            bruteForce = true;
          }
        }

        /* Error finding a Handler for the Link - Rethrow */
        catch (Exception e) {
          final boolean showError[] = new boolean[] { true };

          /* Give user a chance to log in */
          if (e instanceof AuthenticationRequiredException && !monitor.isCanceled() && !Controller.getDefault().isShuttingDown()) {
            final Shell shell = getShell();
            if (shell != null && !shell.isDisposed()) {
              boolean locked = Controller.getDefault().getLoginDialogLock().tryLock();
              if (locked) {
                try {
                  final AuthenticationRequiredException authEx = (AuthenticationRequiredException) e;
                  JobRunner.runSyncedInUIThread(shell, new Runnable() {
                    public void run() {
                      try {

                        /* Return on Cancelation or shutdown or deletion */
                        if (monitor.isCanceled() || Controller.getDefault().isShuttingDown())
                          return;

                        /* Credentials might have been provided meanwhile in another dialog */
                        try {
                          URI normalizedUri = URIUtils.normalizeUri(link, true);
                          if (Owl.getConnectionService().getAuthCredentials(normalizedUri, authEx.getRealm()) != null) {
                            importFromOnlineResource(link);
                            showError[0] = false;
                            return;
                          }
                        } catch (CredentialsException exe) {
                          Activator.getDefault().getLog().log(exe.getStatus());
                        }

                        /* Show Login Dialog */
                        LoginDialog login = new LoginDialog(shell, link, authEx.getRealm());
                        if (login.open() == Window.OK && !monitor.isCanceled() && !Controller.getDefault().isShuttingDown()) {
                          importFromOnlineResource(link);
                          showError[0] = false;
                        }
                      } catch (InvocationTargetException e) {
                        /* Ignore - Error will be handled outside already */
                      } catch (InterruptedException e) {
                        /* Ignore - Error will be handled outside already */
                      }
                    }
                  });
                } finally {
                  Controller.getDefault().getLoginDialogLock().unlock();
                }
              }
            }
          }

          /* Rethrow Exception */
          if (showError[0])
            throw new InvocationTargetException(e);
        } finally {

          /* Reset Field in case of error or cancellation */
          if (canceled || error != null)
            fCurrentProgressMonitor = null;

          /* Close Input Stream */
          if (in != null) {
            try {
              if ((canceled || error != null) && in instanceof IAbortable)
                ((IAbortable) in).abort();
              else
                in.close();
            } catch (IOException e) {
              throw new InvocationTargetException(e);
            }
          }
        }

        /* Scan remote Resource for Links and valid Feeds */
        if (bruteForce && !monitor.isCanceled() && !Controller.getDefault().isShuttingDown()) {
          try {
            importFromOnlineResourceBruteforce(link, monitor, false, false);
          } catch (Exception e) {
            throw new InvocationTargetException(e);
          } finally {
            fCurrentProgressMonitor = null;
          }
        }

        /* Done */
        monitor.done();
        fCurrentProgressMonitor = null;
      }
    };

    /* Run Operation in Background and allow for Cancellation */
    getContainer().run(true, true, runnable);
  }

  private void importFromGoogleReader() throws InvocationTargetException, InterruptedException {
    IRunnableWithProgress runnable = new IRunnableWithProgress() {
      public void run(final IProgressMonitor monitor) throws InvocationTargetException {
        InputStream in = null;
        boolean canceled = false;
        Exception error = null;
        try {
          monitor.beginTask(Messages.ImportElementsPage_IMPORT_GOOGLE_READER, IProgressMonitor.UNKNOWN);
          monitor.subTask(Messages.ImportElementsPage_CONNECTING);
          fCurrentProgressMonitor = monitor;

          /* Return on Cancellation */
          if (monitor.isCanceled() || Controller.getDefault().isShuttingDown()) {
            canceled = true;
            return;
          }

          /* Obtain Google Account Credentials */
          ICredentials credentials = Owl.getConnectionService().getAuthCredentials(URI.create(SyncUtils.GOOGLE_LOGIN), null);
          if (credentials == null) {
            canceled = true;
            return;
          }

          /* Obtain Auth Token */
          String googleAuthToken = SyncUtils.getGoogleAuthToken(credentials.getUsername(), credentials.getPassword(), monitor);

          /* Open Stream */
          in = openStream(URI.create(GOOGLE_READER_OPML_URI), monitor, INITIAL_CON_TIMEOUT, false, false, googleAuthToken);

          /* Return on Cancellation */
          if (monitor.isCanceled() || Controller.getDefault().isShuttingDown()) {
            canceled = true;
            return;
          }

          /* Try to Import */
          try {
            final List<? extends IEntity> types = Owl.getInterpreter().importFrom(in);

            /* Return on Cancellation */
            if (monitor.isCanceled() || Controller.getDefault().isShuttingDown()) {
              canceled = true;
              return;
            }

            /* Show in UI */
            JobRunner.runInUIThread(getShell(), new Runnable() {
              public void run() {
                setImportedElements(types);
                updateMessage(true);
              }
            });
          }

          /* Error Importing from File - Try Bruteforce then */
          catch (Exception e) {
            error = e;
          }
        }

        /* Error finding a Handler for the Link - Rethrow */
        catch (Exception e) {
          throw new InvocationTargetException(e);
        } finally {

          /* Reset Field in case of error or cancellation */
          if (canceled || error != null)
            fCurrentProgressMonitor = null;

          /* Close Input Stream */
          if (in != null) {
            try {
              if ((canceled || error != null) && in instanceof IAbortable)
                ((IAbortable) in).abort();
              else
                in.close();
            } catch (IOException e) {
              throw new InvocationTargetException(e);
            }
          }
        }

        /* Done */
        monitor.done();
        fCurrentProgressMonitor = null;
      }
    };

    /* Run Operation in Background and allow for Cancellation */
    getContainer().run(true, true, runnable);
  }

  private void importFromKeywordSearch(final String keywords, final boolean isLocalizedSearch) throws Exception {
    IRunnableWithProgress runnable = new IRunnableWithProgress() {
      public void run(IProgressMonitor monitor) throws InvocationTargetException {
        try {
          monitor.beginTask(Messages.ImportElementsPage_SEARCHING_FOR_FEEDS, IProgressMonitor.UNKNOWN);
          monitor.subTask(Messages.ImportElementsPage_CONNECTING);
          fCurrentProgressMonitor = monitor;

          /* Build Link for Keyword-Feed Search */
          String linkVal = Controller.getDefault().toFeedSearchLink(keywords);

          /* Return on Cancellation */
          if (monitor.isCanceled() || Controller.getDefault().isShuttingDown())
            return;

          /* Scan remote Resource for Links and valid Feeds */
          importFromOnlineResourceBruteforce(new URI(linkVal), monitor, true, isLocalizedSearch);
        } catch (Exception e) {
          throw new InvocationTargetException(e);
        } finally {
          monitor.done();
          fCurrentProgressMonitor = null;
        }
      }
    };

    /* Run Operation in Background and allow for Cancellation */
    getContainer().run(true, true, runnable);
  }

  private void importFromOnlineResourceBruteforce(URI resourceLink, IProgressMonitor monitor, final boolean isKeywordSearch, boolean isLocalizedSearch) throws ConnectionException, IOException {

    /* Read Content */
    Pair<String, URI> result = readContent(resourceLink, isLocalizedSearch, monitor);
    if (result == null)
      return;

    String content = result.getFirst();
    resourceLink = result.getSecond();

    /* Add the used Link at first if the user has typed in a valid feed address */
    List<String> links = new ArrayList<String>();
    if (!isKeywordSearch)
      links.add(resourceLink.toString());

    /* Extract Links from Content */
    if (StringUtils.isSet(content))
      links.addAll(RegExUtils.extractLinksFromText(content, false));

    /* Sort List: First process likely feeds, then others */
    final String resourceLinkValue = resourceLink.toString();
    Collections.sort(links, new Comparator<String>() {
      public int compare(String o1, String o2) {

        /* Check common feed patterns in URL */
        if (URIUtils.looksLikeFeedLink(o1, false))
          return -1;
        else if (URIUtils.looksLikeFeedLink(o2, false))
          return 1;

        /* Check Origin from same Domain */
        if (!isKeywordSearch) {
          if (o1.contains(resourceLinkValue))
            return -1;
          else if (o2.contains(resourceLinkValue))
            return 1;
        }

        return -1;
      }
    });

    /* If this is not a keyword search, add the top most feed entry first */
    if (!isKeywordSearch) {
      URI feed = CoreUtils.findFeed(new BufferedReader(new StringReader(content)), resourceLink);
      if (feed != null) {
        String feedStr = feed.toString();
        if (links.contains(feedStr))
          links.remove(feedStr);
        links.add(0, feedStr);
      }
    }

    /* Look for Links */
    importFromLinksBruteforce(links, monitor);
  }

  @SuppressWarnings("null")
  private void importFromLinksBruteforce(List<String> links, IProgressMonitor monitor) {

    /* Return on Cancellation */
    if (monitor.isCanceled() || Controller.getDefault().isShuttingDown())
      return;

    /* Update Task Information */
    monitor.beginTask(Messages.ImportElementsPage_SEARCHING_FOR_FEEDS, links.size());
    monitor.subTask(Messages.ImportElementsPage_FETCHING_RESULTS);

    /* A Root to add Found Bookmarks into */
    final IFolder defaultRootFolder = Owl.getModelFactory().createFolder(null, null, Messages.ImportElementsPage_BOOKMARKS);
    defaultRootFolder.setProperty(ITypeImporter.TEMPORARY_FOLDER, true);

    /* For Each Link of the Queue - try to interpret as Feed */
    int counter = 0;
    final List<String> foundBookMarkNames = new ArrayList<String>();
    IBookMarkDAO dao = DynamicDAO.getDAO(IBookMarkDAO.class);
    for (String feedLinkVal : links) {
      monitor.worked(1);

      InputStream in = null;
      boolean canceled = false;
      Exception error = null;
      try {
        URI feedLink = new URI(feedLinkVal);

        /* Report Progress Back To User */
        if (counter == 1)
          monitor.subTask(Messages.ImportElementsPage_SINGLE_RESULT);
        else if (counter > 1)
          monitor.subTask(NLS.bind(Messages.ImportElementsPage_N_RESULTS, counter));

        /* Ignore if already present in Subscriptions List (ignoring trailing slashes) */
        if (dao.exists(new FeedLinkReference(feedLink)))
          continue;
        else if (feedLinkVal.endsWith("/") && dao.exists(new FeedLinkReference(new URI(feedLinkVal.substring(0, feedLinkVal.length() - 1))))) //$NON-NLS-1$
          continue;
        else if (!feedLinkVal.endsWith("/") && dao.exists(new FeedLinkReference(new URI(feedLinkVal + "/")))) //$NON-NLS-1$ //$NON-NLS-2$
          continue;

        /* Return on Cancellation */
        if (monitor.isCanceled() || Controller.getDefault().isShuttingDown())
          break;

        /* Open Stream to potential Feed */
        in = openStream(feedLink, monitor, FEED_CON_TIMEOUT, false, false, null);

        /* Return on Cancellation */
        if (monitor.isCanceled() || Controller.getDefault().isShuttingDown()) {
          canceled = true;
          break;
        }

        /* Try to interpret as Feed */
        IFeed feed = Owl.getModelFactory().createFeed(null, feedLink);
        Owl.getInterpreter().interpret(in, feed, null);
        fLoadedFeedCache.put(feedLink, feed);

        /* Return on Cancellation */
        if (monitor.isCanceled() || Controller.getDefault().isShuttingDown()) {
          canceled = true;
          break;
        }

        /* Add as Result if Feed contains News */
        if (!feed.getNews().isEmpty() && StringUtils.isSet(feed.getTitle())) {
          String title = feed.getTitle();
          boolean sameTitleExists = foundBookMarkNames.contains(title);
          if (sameTitleExists && StringUtils.isSet(feed.getFormat()))
            title = NLS.bind(Messages.ImportElementsPage_FEED_TITLE, title, feed.getFormat());

          final IBookMark bookmark = Owl.getModelFactory().createBookMark(null, defaultRootFolder, new FeedLinkReference(feedLink), title);
          foundBookMarkNames.add(bookmark.getName());
          counter++;

          if (StringUtils.isSet(feed.getDescription()))
            bookmark.setProperty(ITypeImporter.DESCRIPTION_KEY, feed.getDescription());

          if (feed.getHomepage() != null)
            bookmark.setProperty(ITypeImporter.HOMEPAGE_KEY, feed.getHomepage());

          /* Directly show in Viewer */
          JobRunner.runInUIThread(getShell(), new Runnable() {
            public void run() {
              addImportedElement(bookmark);
            }
          });
        }
      }

      /* Ignore Errors (likely not a Feed then) */
      catch (Exception e) {
        error = e;
      }

      /* Close Stream */
      finally {

        /* Close Input Stream */
        if (in != null) {
          try {
            if ((canceled || error != null) && in instanceof IAbortable)
              ((IAbortable) in).abort();
            else
              in.close();
          } catch (IOException e) {
            /* Ignore Silently */
          }
        }
      }
    }

    /* Inform if no feeds have been found */
    if (counter == 0) {
      JobRunner.runInUIThread(getShell(), new Runnable() {
        public void run() {
          setMessage(Messages.ImportElementsPage_NO_FEEDS_FOUND, IMessageProvider.INFORMATION);
        }
      });
    }
  }

  private Pair<String, URI> readContent(URI link, boolean isLocalizedSearch, IProgressMonitor monitor) throws ConnectionException, IOException {
    InputStream in = null;
    try {

      /* Return on Cancellation */
      if (monitor.isCanceled() || Controller.getDefault().isShuttingDown())
        return null;

      /* Open Stream */
      in = openStream(link, monitor, INITIAL_CON_TIMEOUT, true, isLocalizedSearch, null);

      /* Return on Cancellation */
      if (monitor.isCanceled() || Controller.getDefault().isShuttingDown())
        return null;

      /* Read Content */
      String content = StringUtils.readString(new InputStreamReader(in));

      /* Return actual URI that was connected to (supporting redirects) */
      if (in instanceof HttpConnectionInputStream)
        return Pair.create(content, ((HttpConnectionInputStream) in).getLink());

      /* Otherwise just use input URI */
      return Pair.create(content, link);
    } finally {
      if (in instanceof IAbortable)
        ((IAbortable) in).abort();
      else if (in != null)
        in.close();
    }
  }

  private InputStream openStream(URI link, IProgressMonitor monitor, int timeout, boolean setAcceptLanguage, boolean isLocalized, String authToken) throws ConnectionException {
    IProtocolHandler handler = Owl.getConnectionService().getHandler(link);

    Map<Object, Object> properties = new HashMap<Object, Object>();
    properties.put(IConnectionPropertyConstants.CON_TIMEOUT, timeout);

    /* Set Authorization Header if required */
    if (StringUtils.isSet(authToken)) {
      Map<String, String> headers = new HashMap<String, String>();
      headers.put("Authorization", SyncUtils.getGoogleAuthorizationHeader(authToken)); //$NON-NLS-1$
      properties.put(IConnectionPropertyConstants.HEADERS, headers);
    }

    /* Set the Accept-Language Header */
    if (setAcceptLanguage) {
      StringBuilder languageHeader = new StringBuilder();
      String clientLanguage = Locale.getDefault().getLanguage();

      /* Set Both English and Client Locale */
      if (!isLocalized) {
        languageHeader.append(DEFAULT_LANGUAGE);
        if (StringUtils.isSet(clientLanguage) && !DEFAULT_LANGUAGE.equals(clientLanguage))
          languageHeader.append(",").append(clientLanguage); //$NON-NLS-1$
      }

      /* Only set Client Locale */
      else {
        if (StringUtils.isSet(clientLanguage))
          languageHeader.append(clientLanguage);
        else
          languageHeader.append(DEFAULT_LANGUAGE);
      }

      properties.put(IConnectionPropertyConstants.ACCEPT_LANGUAGE, languageHeader.toString());
    }

    return handler.openStream(link, monitor, properties);
  }

  /* Updates Caches and Shows Elements */
  private void setImportedElements(List<? extends IEntity> types) {
    List<IFolderChild> folderChilds = new ArrayList<IFolderChild>();
    for (IEntity type : types) {
      if (type instanceof IFolderChild)
        folderChilds.add((IFolderChild) type);
      else if (type instanceof ILabel)
        fLabels.add((ILabel) type);
      else if (type instanceof ISearchFilter)
        fFilters.add((ISearchFilter) type);
      else if (type instanceof IPreference)
        fPreferences.add((IPreference) type);
    }

    /* Re-Add Filter if necessary */
    if (!fHideExistingCheck.getSelection()) {
      fHideExistingCheck.setSelection(true);
      fViewer.addFilter(fExistingFilter);
    }

    /* Apply as Input */
    fViewer.setInput(folderChilds);
    OwlUI.setAllChecked(fViewer.getTree(), true);
    fExistingFilter.clear();
  }

  /* Adds a IFolderChild to the Viewer and updates caches */
  @SuppressWarnings("unchecked")
  private void addImportedElement(IFolderChild child) {
    Object input = fViewer.getInput();
    ((List) input).add(child);
    fViewer.add(input, child);
    fViewer.setChecked(child, true);
    fViewer.reveal(child);
  }
}
TOP

Related Classes of org.rssowl.ui.internal.dialogs.importer.ImportElementsPage$ExistingBookmarkFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.