Package org.eclipse.wst.sse.ui.internal.spelling

Source Code of org.eclipse.wst.sse.ui.internal.spelling.SpellcheckStrategy$SpellingProblemCollector

/*******************************************************************************
* Copyright (c) 2006, 2009 IBM Corporation and others.
* 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.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*    
*******************************************************************************/
package org.eclipse.wst.sse.ui.internal.spelling;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.reconciler.DirtyRegion;
import org.eclipse.jface.text.reconciler.IReconcileStep;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.IAnnotationModelExtension2;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector;
import org.eclipse.ui.texteditor.spelling.SpellingAnnotation;
import org.eclipse.ui.texteditor.spelling.SpellingContext;
import org.eclipse.ui.texteditor.spelling.SpellingProblem;
import org.eclipse.ui.texteditor.spelling.SpellingService;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.parser.ForeignRegion;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection;
import org.eclipse.wst.sse.core.utils.StringUtils;
import org.eclipse.wst.sse.ui.internal.ExtendedConfigurationBuilder;
import org.eclipse.wst.sse.ui.internal.Logger;
import org.eclipse.wst.sse.ui.internal.reconcile.ReconcileAnnotationKey;
import org.eclipse.wst.sse.ui.internal.reconcile.StructuredReconcileStep;
import org.eclipse.wst.sse.ui.internal.reconcile.StructuredTextReconcilingStrategy;
import org.eclipse.wst.sse.ui.internal.reconcile.TemporaryAnnotation;

/**
* A reconciling strategy that queries the SpellingService using its default
* engine. Results are show as temporary annotations.
*
* @since 1.1
*/
public class SpellcheckStrategy extends StructuredTextReconcilingStrategy {

  class SpellCheckPreferenceListener implements IPropertyChangeListener {
    private boolean isInterestingProperty(Object property) {
      return SpellingService.PREFERENCE_SPELLING_ENABLED.equals(property) || SpellingService.PREFERENCE_SPELLING_ENGINE.equals(property);
    }

    public void propertyChange(PropertyChangeEvent event) {
      if (isInterestingProperty(event.getProperty())) {
        if (event.getOldValue() == null || event.getNewValue() == null || !event.getNewValue().equals(event.getOldValue())) {
          reconcile();
        }
      }
    }
  }

  private class SpellingProblemCollector implements ISpellingProblemCollector {
    List annotations = new ArrayList();

    public void accept(SpellingProblem problem) {
      if (isInterestingProblem(problem)) {
        TemporaryAnnotation annotation = new TemporaryAnnotation(new Position(problem.getOffset(), problem.getLength()), SpellingAnnotation.TYPE, problem.getMessage(), fReconcileAnnotationKey);

        SpellingQuickAssistProcessor quickAssistProcessor = new SpellingQuickAssistProcessor();
        quickAssistProcessor.setSpellingProblem(problem);
        annotation.setAdditionalFixInfo(quickAssistProcessor);
        annotations.add(annotation);
        if (_DEBUG_SPELLING_PROBLEMS) {
          Logger.log(Logger.INFO, problem.getMessage());
        }
      }
    }

    public void beginCollecting() {
    }

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

    public void endCollecting() {
    }

    Annotation[] getAnnotations() {
      return (Annotation[]) annotations.toArray(new Annotation[annotations.size()]);
    }
  }

  private static final boolean _DEBUG_SPELLING = Boolean.valueOf(Platform.getDebugOption("org.eclipse.wst.sse.ui/debug/reconcilerSpelling")).booleanValue(); //$NON-NLS-1$
  private static final boolean _DEBUG_SPELLING_PROBLEMS = Boolean.valueOf(Platform.getDebugOption("org.eclipse.wst.sse.ui/debug/reconcilerSpelling/showProblems")).booleanValue(); //$NON-NLS-1$

  private static final String EXTENDED_BUILDER_TYPE_CONTEXTS = "spellingregions"; //$NON-NLS-1$
  private static final String KEY_CONTENT_TYPE = "org.eclipse.wst.sse.ui.temp.spelling"; //$NON-NLS-1$

  private String fContentTypeId = null;

  private SpellingProblemCollector fProblemCollector = new SpellingProblemCollector();

  /*
   * Keying our Temporary Annotations based on the partition doesn't help
   * this strategy to only remove its own TemporaryAnnotations since it's
   * possibly run on all partitions. Instead, set the key to use an
   * arbitrary partition type that we can check for using our own
   * implementation of getAnnotationsToRemove(DirtyRegion).
   */
  ReconcileAnnotationKey fReconcileAnnotationKey;

  private IPropertyChangeListener fSpellCheckPreferenceListener;

  private SpellingContext fSpellingContext;

  private String[] fSupportedTextRegionContexts;
  private IReconcileStep fSpellingStep = new StructuredReconcileStep() {
  };

  public SpellcheckStrategy(ISourceViewer viewer, String contentTypeId) {
    super(viewer);
    fContentTypeId = contentTypeId;

    fSpellingContext = new SpellingContext();
    IContentType contentType = Platform.getContentTypeManager().getContentType(fContentTypeId);
    fSpellingContext.setContentType(contentType);
    fReconcileAnnotationKey = new ReconcileAnnotationKey(fSpellingStep, KEY_CONTENT_TYPE, ReconcileAnnotationKey.PARTIAL);

    /**
     * Inherit spelling region rules
     */
    List contexts = new ArrayList();
    IContentType testType = contentType;
    while (testType != null) {
      String[] textRegionContexts = ExtendedConfigurationBuilder.getInstance().getDefinitions(EXTENDED_BUILDER_TYPE_CONTEXTS, testType.getId());
      for (int j = 0; j < textRegionContexts.length; j++) {
        contexts.addAll(Arrays.asList(StringUtils.unpack(textRegionContexts[j])));
      }
      testType = testType.getBaseType();
    }
    fSupportedTextRegionContexts = (String[]) contexts.toArray(new String[contexts.size()]);

    fSpellCheckPreferenceListener = new SpellCheckPreferenceListener();
  }

  protected boolean containsStep(IReconcileStep step) {
    return fSpellingStep.equals(step);
  }

  public void createReconcileSteps() {

  }

  private TemporaryAnnotation[] getSpellingAnnotationsToRemove(IRegion region) {
    List toRemove = new ArrayList();
    IAnnotationModel annotationModel = getAnnotationModel();
    // can be null when closing the editor
    if (annotationModel != null) {
      Iterator i = null;
      boolean annotationOverlaps = false;
      if (annotationModel instanceof IAnnotationModelExtension2) {
        i = ((IAnnotationModelExtension2) annotationModel).getAnnotationIterator(region.getOffset(), region.getLength(), true, true);
        annotationOverlaps = true;
      }
      else {
        i = annotationModel.getAnnotationIterator();
      }

      while (i.hasNext()) {
        Object obj = i.next();
        if (!(obj instanceof TemporaryAnnotation))
          continue;

        TemporaryAnnotation annotation = (TemporaryAnnotation) obj;
        ReconcileAnnotationKey key = (ReconcileAnnotationKey) annotation.getKey();

        // then if this strategy knows how to add/remove this
        // partition type
        if (key != null && key.equals(fReconcileAnnotationKey)) {
          if (key.getScope() == ReconcileAnnotationKey.PARTIAL && (annotationOverlaps || annotation.getPosition().overlapsWith(region.getOffset(), region.getLength()))) {
            toRemove.add(annotation);
          }
          else if (key.getScope() == ReconcileAnnotationKey.TOTAL) {
            toRemove.add(annotation);
          }
        }
      }
    }

    return (TemporaryAnnotation[]) toRemove.toArray(new TemporaryAnnotation[toRemove.size()]);
  }

  /**
   * Judge whether a spelling problem is "interesting". Accept any regions
   * that are explicitly allowed, and since valid prose areas are rarely in
   * a complicated document region, accept any document region with more
   * than one text region and reject any document regions containing foreign
   * text regions.
   *
   * @param problem
   *            a SpellingProblem
   * @return whether the collector should accept the given SpellingProblem
   */
  protected boolean isInterestingProblem(SpellingProblem problem) {
    IDocument document = getDocument();
    if (document instanceof IStructuredDocument) {
      /*
       * If the error is in a read-only section, ignore it. The user
       * won't be able to correct it.
       */
      if (((IStructuredDocument) document).containsReadOnly(problem.getOffset(), problem.getLength()))
        return false;

      IStructuredDocumentRegion documentRegion = ((IStructuredDocument) document).getRegionAtCharacterOffset(problem.getOffset());
      if (documentRegion != null) {
        ITextRegion textRegion = documentRegion.getRegionAtCharacterOffset(problem.getOffset());
        //if the region is not null, and is a supported context and is not a collection of regions,
        //  and it should be spell-checked, then spell check it.
        if (textRegion != null && isSupportedContext(textRegion.getType()) && !(textRegion instanceof ITextRegionCollection) && shouldSpellcheck(problem.getOffset())) {
          return true;
        }
        if (documentRegion.getFirstRegion() instanceof ForeignRegion)
          return false;
//        [192572] Simple regions were being spellchecked just for the sake of them being simple
//        if (documentRegion.getRegions().size() == 1)
//          return true;
        return false;
      }
    }
    return true;
  }


  private boolean isSupportedContext(String type) {
    boolean isSupported = false;
    if (fSupportedTextRegionContexts.length > 0) {
      for (int i = 0; i < fSupportedTextRegionContexts.length; i++) {
        if (type.equals(fSupportedTextRegionContexts[i])) {
          isSupported = true;
          break;
        }
      }
    }
    else {
      isSupported = true;
    }
    return isSupported;
  }

  public void reconcile() {
    IDocument document = getDocument();
    if (document != null) {
      IAnnotationModel annotationModel = getAnnotationModel();
      if (annotationModel != null) {
        IRegion documentRegion = new Region(0, document.getLength());
        spellCheck(documentRegion, documentRegion, annotationModel);
      }
    }
  }

  /**
   * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.reconciler.DirtyRegion,
   *      org.eclipse.jface.text.IRegion)
   */
  public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
    if (isCanceled())
      return;

    IAnnotationModel annotationModel = getAnnotationModel();

    IDocument document = getDocument();
    if (document != null) {
      long time0 = 0;
      if (_DEBUG_SPELLING) {
        time0 = System.currentTimeMillis();
      }
      /**
       * Apparently the default spelling engine has noticeable overhead
       * when called multiple times in rapid succession. It's faster to
       * check the entire dirty region at once since we know that we're
       * not differentiating by partition.
       *
       * https://bugs.eclipse.org/bugs/show_bug.cgi?id=192530
       */
      if (_DEBUG_SPELLING) {
        Logger.log(Logger.INFO, "Spell Checking [" + dirtyRegion.getOffset() + ":" + dirtyRegion.getLength() + "] : " + (System.currentTimeMillis() - time0));
      }
      if (annotationModel != null) {
        spellCheck(dirtyRegion, dirtyRegion, annotationModel);
      }
    }
  }

  private void spellCheck(IRegion dirtyRegion, IRegion regionToBeChecked, IAnnotationModel annotationModel) {
    if (annotationModel == null)
      return;

    TemporaryAnnotation[] annotationsToRemove;
    Annotation[] annotationsToAdd;
    annotationsToRemove = getSpellingAnnotationsToRemove(regionToBeChecked);

    if (_DEBUG_SPELLING_PROBLEMS) {
      Logger.log(Logger.INFO, "Spell checking [" + regionToBeChecked.getOffset() + "-" + (regionToBeChecked.getOffset() + regionToBeChecked.getLength()) + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    }
    if (getDocument() != null) {
      EditorsUI.getSpellingService().check(getDocument(), new IRegion[]{regionToBeChecked}, fSpellingContext, fProblemCollector, null);
    }
    annotationsToAdd = fProblemCollector.getAnnotations();
    fProblemCollector.clear();


    if (annotationModel instanceof IAnnotationModelExtension) {
      IAnnotationModelExtension modelExtension = (IAnnotationModelExtension) annotationModel;
      Map annotationsToAddMap = new HashMap();
      for (int i = 0; i < annotationsToAdd.length; i++) {
        annotationsToAddMap.put(annotationsToAdd[i], ((TemporaryAnnotation) annotationsToAdd[i]).getPosition());
      }
      modelExtension.replaceAnnotations(annotationsToRemove, annotationsToAddMap);
    }

    else {
      for (int j = 0; j < annotationsToAdd.length; j++) {
        annotationModel.addAnnotation(annotationsToAdd[j], ((TemporaryAnnotation) annotationsToAdd[j]).getPosition());
      }
      for (int j = 0; j < annotationsToRemove.length; j++) {
        annotationModel.removeAnnotation(annotationsToRemove[j]);
      }
    }
  }

  /**
   * @param partition
   * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.IRegion)
   */

  public void reconcile(IRegion partition) {
    IDocument document = getDocument();
    if (document != null) {
      IAnnotationModel annotationModel = getAnnotationModel();
      if (annotationModel != null) {
        spellCheck(partition, partition, annotationModel);
      }
    }
  }

  public void setDocument(IDocument document) {
    if (getDocument() != null) {
      EditorsUI.getPreferenceStore().removePropertyChangeListener(fSpellCheckPreferenceListener);
    }

    super.setDocument(document);

    if (getDocument() != null) {
      EditorsUI.getPreferenceStore().addPropertyChangeListener(fSpellCheckPreferenceListener);
    }
  }
 
  /**
   * Decides if the given offset should be spell-checked using an <code>IAdapterFactory</code>
   *
   * @param offset Decide if this offset should be spell-checked
   * @return <code>true</code> if the given <code>offset</code> should be spell-checked,
   * <code>false</code> otherwise.
   */
  private boolean shouldSpellcheck(int offset) {
    boolean decision = true;
   
    IStructuredModel model = null;
    try {
      model = StructuredModelManager.getModelManager().getExistingModelForRead(getDocument());
     
      /* use an an adapter factory to get a spell-check decision maker,
       * and ask it if the  offset should be spell-checked.  It is done
       * this way so content type specific decisions can be made without this
       * plugin being aware of any content type specifics.
       */
      ISpellcheckDelegate delegate = (ISpellcheckDelegate)Platform.getAdapterManager().getAdapter(model, ISpellcheckDelegate.class);
      if(delegate != null) {
        decision = delegate.shouldSpellcheck(offset, model);
      }
    }  finally {
      if(model != null) {
        model.releaseFromRead();
      }
    }
   
    return decision;
  }
}
TOP

Related Classes of org.eclipse.wst.sse.ui.internal.spelling.SpellcheckStrategy$SpellingProblemCollector

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.