/******************************************************************************* * Copyright (c) 2005, 2013 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 * Jens Lukowski/Innoopract - initial renaming/restructuring * *******************************************************************************/ package org.eclipse.wst.sse.ui.internal.contentassist; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.contentassist.IContentAssistSubjectControl; import org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor; import org.eclipse.jface.contentassist.ISubjectControlContextInformationPresenter; import org.eclipse.jface.contentassist.ISubjectControlContextInformationValidator; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.TextPresentation; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.contentassist.IContextInformationExtension; import org.eclipse.jface.text.contentassist.IContextInformationPresenter; import org.eclipse.jface.text.contentassist.IContextInformationValidator; import org.eclipse.swt.graphics.Image; import org.eclipse.wst.sse.ui.contentassist.StructuredContentAssistProcessor; import org.eclipse.wst.sse.ui.internal.IReleasable; import org.eclipse.wst.sse.ui.internal.Logger; /** * <p>A processor that aggregates the proposals of multiple other processors. * When proposals are requested, the contained processors are queried in the * order they were added to the compound object. Copied from * org.eclipse.jdt.internal.ui.text.CompoundContentAssistProcessor. * Modification was made to add a dispose() method.</p> */ public class CompoundContentAssistProcessor implements IContentAssistProcessor, ISubjectControlContentAssistProcessor, IReleasable { /** the compound processors */ private final Set fProcessors = new LinkedHashSet(); /** Aggregated error message from the compound processors */ private String fErrorMessage; /** * Creates a new instance. */ public CompoundContentAssistProcessor() { } /** * Creates a new instance with one child processor. * * @param processor * the processor to add */ public CompoundContentAssistProcessor(IContentAssistProcessor processor) { add(processor); } /** * Adds a processor to this compound processor. * * @param processor * the processor to add */ public void add(IContentAssistProcessor processor) { Assert.isNotNull(processor); fProcessors.add(processor); } /** * @param processor check to see if this {@link IContentAssistProcessor} is one * in this compound processor. * * @return <code>true</code> if this compound processor contains the given processor, * <code>false</code> otherwise */ public boolean containsProcessor(IContentAssistProcessor processor) { return fProcessors.contains(processor); } /** * Removes a processor from this compound processor. * * @param processor * the processor to remove */ public void remove(IContentAssistProcessor processor) { fProcessors.remove(processor); } /** * Creates a new instance and adds all specified processors. * * @param processors */ public CompoundContentAssistProcessor(IContentAssistProcessor[] processors) { for (int i = 0; i < processors.length; i++) { add(processors[i]); } } /* * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, * int) */ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentOffset) { fErrorMessage = null; List ret = new LinkedList(); for (Iterator it = fProcessors.iterator(); it.hasNext();) { IContentAssistProcessor p = (IContentAssistProcessor) it.next(); try { // isolate calls to each processor ICompletionProposal[] proposals = p.computeCompletionProposals(viewer, documentOffset); if (proposals != null && proposals.length > 0) { ret.addAll(Arrays.asList(proposals)); fErrorMessage = null; // Hide previous errors } else { if (fErrorMessage == null && ret.isEmpty()) { String errorMessage = p.getErrorMessage(); if (errorMessage != null) { fErrorMessage = errorMessage; } } } } catch (Exception e) { Logger.logException(e); } } return (ICompletionProposal[]) ret.toArray(new ICompletionProposal[ret.size()]); } /** * {@inheritDoc} * <p> * The returned objects are wrapper objects around the real information * containers. * </p> * * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, * int) */ public IContextInformation[] computeContextInformation(ITextViewer viewer, int documentOffset) { fErrorMessage = null; List ret = new LinkedList(); for (Iterator it = fProcessors.iterator(); it.hasNext();) { IContentAssistProcessor p = (IContentAssistProcessor) it.next(); IContextInformation[] informations = p.computeContextInformation(viewer, documentOffset); if (informations != null && informations.length > 0) { for (int i = 0; i < informations.length; i++) ret.add(new WrappedContextInformation(informations[i], p)); fErrorMessage = null; // Hide previous errors } else { if (fErrorMessage == null && ret.isEmpty()) { String errorMessage = p.getErrorMessage(); if (errorMessage != null) { fErrorMessage = errorMessage; } } } } return (IContextInformation[]) ret.toArray(new IContextInformation[ret.size()]); } /* * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters() */ public char[] getCompletionProposalAutoActivationCharacters() { Set ret = new LinkedHashSet(); for (Iterator it = fProcessors.iterator(); it.hasNext();) { IContentAssistProcessor p = (IContentAssistProcessor) it.next(); char[] chars = p.getCompletionProposalAutoActivationCharacters(); if (chars != null) for (int i = 0; i < chars.length; i++) ret.add(new Character(chars[i])); } char[] chars = new char[ret.size()]; int i = 0; for (Iterator it = ret.iterator(); it.hasNext(); i++) { Character ch = (Character) it.next(); chars[i] = ch.charValue(); } return chars; } /* * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters() */ public char[] getContextInformationAutoActivationCharacters() { Set ret = new LinkedHashSet(); for (Iterator it = fProcessors.iterator(); it.hasNext();) { IContentAssistProcessor p = (IContentAssistProcessor) it.next(); char[] chars = p.getContextInformationAutoActivationCharacters(); if (chars != null) for (int i = 0; i < chars.length; i++) ret.add(new Character(chars[i])); } char[] chars = new char[ret.size()]; int i = 0; for (Iterator it = ret.iterator(); it.hasNext(); i++) { Character ch = (Character) it.next(); chars[i] = ch.charValue(); } return chars; } /** * Returns the error message of one of * contained processor if any, or <code>null</code> if no processor has an * error message. * * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage() * @return {@inheritDoc} */ public String getErrorMessage() { return fErrorMessage; } /** * {@inheritDoc} * <p> * The returned validator is a wrapper around the validators provided by * the child processors. * </p> * * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator() */ public IContextInformationValidator getContextInformationValidator() { boolean hasValidator = false; boolean hasPresenter = false; boolean hasExtension = false; Iterator itp = fProcessors.iterator(); while (itp.hasNext() && (!hasPresenter || !hasExtension)) { IContentAssistProcessor p = (IContentAssistProcessor) itp.next(); IContextInformationValidator v = p.getContextInformationValidator(); if (v != null) { hasValidator = true; if (v instanceof IContextInformationPresenter) { hasPresenter = true; } if (v instanceof ISubjectControlContextInformationPresenter || v instanceof ISubjectControlContextInformationValidator) { hasExtension = true; } } } CompoundContentAssistValidator validator = null; if (hasPresenter && hasExtension) validator = new CompoundContentAssistValidatorPresenterEx(); else if (hasPresenter) validator = new CompoundContentAssistValidatorPresenter(); else if (hasExtension) validator = new CompoundContentAssistValidatorEx(); else if (hasValidator) validator = new CompoundContentAssistValidator(); if (validator != null) for (Iterator it = fProcessors.iterator(); it.hasNext();) { IContentAssistProcessor p = (IContentAssistProcessor) it.next(); IContextInformationValidator v = p.getContextInformationValidator(); if (v != null) validator.add(v); } return validator; } /* * @see ISubjectControlContentAssistProcessor#computeCompletionProposals(IContentAssistSubjectControl, * int) */ public ICompletionProposal[] computeCompletionProposals(IContentAssistSubjectControl contentAssistSubjectControl, int documentOffset) { fErrorMessage = null; List ret = new LinkedList(); for (Iterator it = fProcessors.iterator(); it.hasNext();) { Object o = it.next(); if (o instanceof ISubjectControlContentAssistProcessor) { ISubjectControlContentAssistProcessor p = (ISubjectControlContentAssistProcessor) o; ICompletionProposal[] proposals = p.computeCompletionProposals(contentAssistSubjectControl, documentOffset); if (proposals != null && proposals.length > 0) { ret.addAll(Arrays.asList(proposals)); fErrorMessage = null; // Hide previous errors } else { if (fErrorMessage == null && ret.isEmpty()) { String errorMessage = p.getErrorMessage(); if (errorMessage != null) { fErrorMessage = errorMessage; } } } } } return (ICompletionProposal[]) ret.toArray(new ICompletionProposal[ret.size()]); } /** * {@inheritDoc} * <p> * The returned objects are wrapper objects around the real information * containers. * </p> * * @see org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.contentassist.IContentAssistSubject, * int) */ public IContextInformation[] computeContextInformation(IContentAssistSubjectControl contentAssistSubjectControl, int documentOffset) { fErrorMessage = null; List ret = new LinkedList(); for (Iterator it = fProcessors.iterator(); it.hasNext();) { Object o = it.next(); if (o instanceof ISubjectControlContentAssistProcessor) { ISubjectControlContentAssistProcessor p = (ISubjectControlContentAssistProcessor) o; IContextInformation[] informations = p.computeContextInformation(contentAssistSubjectControl, documentOffset); if (informations != null && informations.length > 0) { for (int i = 0; i < informations.length; i++) ret.add(new WrappedContextInformation(informations[i], p)); fErrorMessage = null; // Hide previous errors } else { if (fErrorMessage == null && ret.isEmpty()) { String errorMessage = p.getErrorMessage(); if (errorMessage != null) { fErrorMessage = errorMessage; } } } } } return (IContextInformation[]) ret.toArray(new IContextInformation[ret.size()]); } /** * Dispose of any content assist processors that need disposing * @deprecated use {@link #release()} */ public void dispose() { this.release(); } public void install(ITextViewer viewer) { for (Iterator it = fProcessors.iterator(); it.hasNext();) { IContentAssistProcessor p = (IContentAssistProcessor) it.next(); if (p instanceof StructuredContentAssistProcessor) { ((StructuredContentAssistProcessor) p).install(viewer); } } } /** * @see org.eclipse.wst.sse.ui.internal.IReleasable#release() */ public void release() { // go through list of content assist processors and dispose for (Iterator it = fProcessors.iterator(); it.hasNext();) { IContentAssistProcessor p = (IContentAssistProcessor) it.next(); if (p instanceof IReleasable) { ((IReleasable) p).release(); } } } private static class WrappedContextInformation implements IContextInformation, IContextInformationExtension { private IContextInformation fInfo; private IContentAssistProcessor fProcessor; WrappedContextInformation(IContextInformation info, IContentAssistProcessor processor) { fInfo = info; fProcessor = processor; } /* * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { return fInfo.equals(obj); } /* * @see org.eclipse.jface.text.contentassist.IContextInformation#getContextDisplayString() */ public String getContextDisplayString() { return fInfo.getContextDisplayString(); } /* * @see org.eclipse.jface.text.contentassist.IContextInformation#getImage() */ public Image getImage() { return fInfo.getImage(); } /* * @see org.eclipse.jface.text.contentassist.IContextInformation#getInformationDisplayString() */ public String getInformationDisplayString() { return fInfo.getInformationDisplayString(); } /* * @see java.lang.Object#hashCode() */ public int hashCode() { return fInfo.hashCode(); } /* * @see java.lang.Object#toString() */ public String toString() { return fInfo.toString(); } IContentAssistProcessor getProcessor() { return fProcessor; } IContextInformation getContextInformation() { return fInfo; } public int getContextInformationPosition() { int position = -1; if (fInfo instanceof IContextInformationExtension) position = ((IContextInformationExtension)fInfo).getContextInformationPosition(); return position; } } private static class CompoundContentAssistValidator implements IContextInformationValidator { List fValidators = new ArrayList(); IContextInformationValidator fValidator; void add(IContextInformationValidator validator) { fValidators.add(validator); } /* * @see org.eclipse.jface.text.contentassist.IContextInformationValidator#install(org.eclipse.jface.text.contentassist.IContextInformation, * org.eclipse.jface.text.ITextViewer, int) */ public void install(IContextInformation info, ITextViewer viewer, int documentPosition) { // install either the validator in the info, or all validators fValidator = getValidator(info); IContextInformation realInfo = getContextInformation(info); if (fValidator != null) fValidator.install(realInfo, viewer, documentPosition); else { for (Iterator it = fValidators.iterator(); it.hasNext();) { IContextInformationValidator v = (IContextInformationValidator) it.next(); v.install(realInfo, viewer, documentPosition); } } } IContextInformationValidator getValidator(IContextInformation info) { if (info instanceof WrappedContextInformation) { WrappedContextInformation wrap = (WrappedContextInformation) info; return wrap.getProcessor().getContextInformationValidator(); } return null; } IContextInformation getContextInformation(IContextInformation info) { IContextInformation realInfo = info; if (info instanceof WrappedContextInformation) { WrappedContextInformation wrap = (WrappedContextInformation) info; realInfo = wrap.getContextInformation(); } return realInfo; } /* * @see org.eclipse.jface.text.contentassist.IContextInformationValidator#isContextInformationValid(int) */ public boolean isContextInformationValid(int documentPosition) { // use either the validator in the info, or all validators boolean isValid = false; if (fValidator != null) isValid = fValidator.isContextInformationValid(documentPosition); else { for (Iterator it = fValidators.iterator(); it.hasNext();) { IContextInformationValidator v = (IContextInformationValidator) it.next(); isValid |= v.isContextInformationValid(documentPosition); } } return isValid; } } private static class CompoundContentAssistValidatorPresenter extends CompoundContentAssistValidator implements IContextInformationPresenter { public boolean updatePresentation(int offset, TextPresentation presentation) { // use either the validator in the info, or all validators boolean presentationUpdated = false; if (fValidator instanceof IContextInformationPresenter) presentationUpdated = ((IContextInformationPresenter) fValidator).updatePresentation(offset, presentation); else { for (Iterator it = fValidators.iterator(); it.hasNext();) { IContextInformationValidator v = (IContextInformationValidator) it.next(); if (v instanceof IContextInformationPresenter) presentationUpdated |= ((IContextInformationPresenter) v).updatePresentation(offset, presentation); } } return presentationUpdated; } } private static class CompoundContentAssistValidatorEx extends CompoundContentAssistValidator implements ISubjectControlContextInformationValidator { /* * @see ISubjectControlContextInformationValidator#install(IContextInformation, * IContentAssistSubjectControl, int) */ public void install(IContextInformation info, IContentAssistSubjectControl contentAssistSubjectControl, int documentPosition) { // install either the validator in the info, or all validators fValidator = getValidator(info); IContextInformation realInfo = getContextInformation(info); if (fValidator instanceof ISubjectControlContextInformationValidator) ((ISubjectControlContextInformationValidator) fValidator).install(realInfo, contentAssistSubjectControl, documentPosition); else { for (Iterator it = fValidators.iterator(); it.hasNext();) { if (it.next() instanceof ISubjectControlContextInformationValidator) ((ISubjectControlContextInformationValidator) it.next()).install(realInfo, contentAssistSubjectControl, documentPosition); } } } } private static class CompoundContentAssistValidatorPresenterEx extends CompoundContentAssistValidatorPresenter implements ISubjectControlContextInformationPresenter, ISubjectControlContextInformationValidator { /* * @see ISubjectControlContextInformationPresenter#install(IContextInformation, * IContentAssistSubjectControl, int) */ public void install(IContextInformation info, IContentAssistSubjectControl contentAssistSubjectControl, int documentPosition) { // install either the validator in the info, or all validators fValidator = getValidator(info); IContextInformation realInfo = getContextInformation(info); if (fValidator instanceof ISubjectControlContextInformationValidator) ((ISubjectControlContextInformationValidator) fValidator).install(realInfo, contentAssistSubjectControl, documentPosition); else { for (Iterator it = fValidators.iterator(); it.hasNext();) { if (it.next() instanceof ISubjectControlContextInformationValidator) ((ISubjectControlContextInformationValidator) it.next()).install(realInfo, contentAssistSubjectControl, documentPosition); } } } } }