/******************************************************************************* * Copyright (c) 2009-2011 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is 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: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.common.text; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import org.eclipse.jface.resource.ImageDescriptor; /** * Text Proposal for Content Assist. * @author Alexey Kazakov */ public class TextProposal { public interface PostProcessing { public void process(TextProposal proposal, String value, int offset); } public static final int R_NONE = 0; public static final int R_JSP_JSF_EL_VARIABLE_ATTRIBUTE_VALUE = 810; public static final int R_JSP_ATTRIBUTE_VALUE = 830; public static final int R_XML_ATTRIBUTE_VALUE = 850; public static final int R_XML_ATTRIBUTE_NAME = 910; public static final int R_TAG_INSERTION = 1210; // This value was changed from 500 to 1210 because it seems that according WTP's value was risen up to 1200 in 3.6 public static final int R_TAG_TEMPLATE = 1250; public static final int R_XML_ATTRIBUTE_VALUE_TEMPLATE = 91; public static final int R_XML_TAG_INSERTION = 91; public static final int R_CLOSE_TAG = 1550; private static final long serialVersionUID = 3257007635692926512L; private Object source; private String label; private String contextInfo; private ImageDescriptor imageDescriptor; private boolean emptyImage = true; private String replacementString; private List<String> alternativeMatches = new ArrayList<String>(); private boolean emptyContextInfo = true; private int relevance = R_NONE; private int position = -1; private boolean autoActivationContentAssistantAfterApplication = true; private String type; private String sourceType; private int start = -1; private int end = -1; PostProcessing postProcessing; private IExecutableTextProposal executable = null; private boolean isFilterable = true; /* (non-Javadoc) * @see java.lang.Object#clone() */ @Override public Object clone() throws CloneNotSupportedException { TextProposal newObject = new TextProposal(); newObject.source = this.source; newObject.label = this.label; newObject.contextInfo = this.contextInfo; newObject.imageDescriptor = this.imageDescriptor; newObject.emptyImage = this.emptyImage; newObject.replacementString = this.replacementString; newObject.emptyContextInfo = this.emptyContextInfo; newObject.relevance = this.relevance; newObject.position = this.position; newObject.autoActivationContentAssistantAfterApplication = this.autoActivationContentAssistantAfterApplication; newObject.type = this.type; newObject.sourceType = this.sourceType; newObject.start = this.start; newObject.end = this.end; return newObject; } public String getType() { return type; } public void setType(String typeName) { this.type = typeName; } public String getSourceType() { return sourceType; } public void setSourceType(String sourceTypeName) { this.sourceType = sourceTypeName; } /** * * @return */ public int getRelevance() { return relevance; } /** * * @param relevance */ public void setRelevance(int relevance) { this.relevance = relevance; } /** * @return */ public String getContextInfo() { return contextInfo; } /** * @return */ public ImageDescriptor getImageDescriptor() { return imageDescriptor; } // CommonUIPlugin.getImageDescriptorRegistry().get /** * @return */ public String getLabel() { String l = label != null ? label : getReplacementString() == null ? "" : getReplacementString(); //$NON-NLS-1$ StringBuilder dispLabel = new StringBuilder(l); if (type != null) { dispLabel.append(" : "); //$NON-NLS-1$ dispLabel.append(type); } if (sourceType != null) { dispLabel.append(" - "); //$NON-NLS-1$ dispLabel.append(sourceType); } return dispLabel.toString(); } /** * @return */ public String getReplacementString() { return replacementString; } /** * @param string */ public void setContextInfo(String string) { contextInfo = string; if(contextInfo != null) { emptyContextInfo = false; } } /** * @param string */ public void setImageDescriptor(ImageDescriptor img) { this.imageDescriptor = img; if(this.imageDescriptor != null) { emptyImage = false; } } /** * @param string */ public void setLabel(String string) { label = string; } /** * @param string */ public void setReplacementString(String string) { replacementString = string; } /** * @return */ public boolean hasContextInfo() { return !emptyContextInfo; } /** * @return */ public boolean hasImage() { return !emptyImage; } /** * @return */ public int getPosition() { return position; } /** * @param i */ public void setPosition(int i) { position = i; } /** * * @param lowerCase */ public void changeCase(boolean lowerCase) { if(lowerCase) { if(label!=null) label = label.toLowerCase(); if(replacementString!=null) replacementString = replacementString.toLowerCase(); } else { if(label!=null) label = label.toUpperCase(); if(replacementString!=null) replacementString = replacementString.toUpperCase(); } } /** * * */ public void removeAutocompleteRequiredAttributes() { int endAttr = replacementString.lastIndexOf('"'); if(endAttr!=-1) { int startAttr = replacementString.substring(0, replacementString.indexOf('"')).lastIndexOf(' '); String newReplacementString = replacementString.substring(0, startAttr); if(endAttr+1<replacementString.length()) { newReplacementString = newReplacementString + replacementString.substring(endAttr + 1); } replacementString = newReplacementString; } } /** * * @return */ public boolean isCloseTag() { return label != null && label.startsWith("/"); //$NON-NLS-1$ } /** * Returns object with apply() method that should be invoked by * AutoContentAssistantProposal.apply() instead super.apply(). * * @return */ public IExecutableTextProposal getExecutable() { return executable; } public void setExecutable(IExecutableTextProposal executable) { this.executable = executable; } /** * Returns true, if this proposal may be removed by the filter * that removes duplicated proposals. * * @return */ public boolean isFilterable() { return isFilterable; } public void setFilterable(boolean value) { isFilterable = value; } /** * @see java.lang.Object#toString() */ public String toString() { StringBuilder buffer = new StringBuilder(); buffer.append("label: "); //$NON-NLS-1$ buffer.append(label); buffer.append("\ncontextInfo: "); //$NON-NLS-1$ buffer.append(contextInfo); buffer.append("\nreplacementString: "); //$NON-NLS-1$ buffer.append(replacementString); return buffer.toString(); } /** * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(Object o) { return label.compareTo(((TextProposal)o).getLabel()); } /** * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { if(obj == this) { return true; } if((!(obj instanceof TextProposal)) || (obj == null)) { return false; } TextProposal anotherProposal = (TextProposal)obj; boolean labelB = false; boolean contextInfoB = false; boolean replacementStringB = false; if(this.label!=null) { labelB = this.label.equals(anotherProposal.getLabel()); } else if(anotherProposal.getLabel()==null) { labelB = true; } if(this.contextInfo!=null) { contextInfoB = this.contextInfo.equals(anotherProposal.getContextInfo()); } else if(anotherProposal.getContextInfo()==null) { contextInfoB = true; } if(this.replacementString!=null) { replacementStringB = this.replacementString.equals(anotherProposal.getReplacementString()); } else if(anotherProposal.getReplacementString()==null) { replacementStringB = true; } return labelB&&contextInfoB&&replacementStringB; } /** * * @return */ public boolean isAutoActivationContentAssistantAfterApplication() { return autoActivationContentAssistantAfterApplication; } /** * */ public void setAutoActivationContentAssistantAfterApplication(boolean autoActivationContentAssistantAfterApplication) { this.autoActivationContentAssistantAfterApplication = autoActivationContentAssistantAfterApplication; } public void setStart(int n) { start = n; } public void setEnd(int n) { end = n; } public int getStart() { return start; } public int getEnd() { return end; } public void setPostProcessing(PostProcessing postProcessing) { this.postProcessing = postProcessing; } public void postProcess(String value, int offset) { if(postProcessing != null) postProcessing.process(this, value, offset); } /** * @return the source */ public Object getSource() { return source; } /** * @param source the source to set */ public void setSource(Object source) { this.source = source; } /** * Returns the first alternative match of the list assigned to this proposal, * or null if the list is empty. * @return */ public String getAlternateMatch() { return alternativeMatches.isEmpty() ? null : alternativeMatches.get(0); } /** * Sets the first alternative match. * * @param alternateMatch */ public void setAlternateMatch(String alternateMatch) { alternativeMatches.remove(alternateMatch); alternativeMatches.add(0, alternateMatch); } /** * Returns all keywords related to this proposal * @return */ public List<String> getAlternativeMatches() { return alternativeMatches; } /** * Checks if template is matching start of any keywords for this proposals. * @param template * @return */ public boolean isAlternativeMatchStart(String template) { if(template.length() > 0) { for (String s: getAlternativeMatches()) { if(s.startsWith(template)) { return true; } } } return false; } public static final Comparator<TextProposal> KB_PROPOSAL_ORDER = new TextProposalComparator(); private static class TextProposalComparator implements Comparator<TextProposal> { public int compare(TextProposal p1, TextProposal p2) { int n1=p1.replacementString.length(), n2=p2.replacementString.length(); for (int i1=0, i2=0; i1<n1 && i2<n2; i1++, i2++) { char c1 = p1.replacementString.charAt(i1); char c2 = p2.replacementString.charAt(i2); if (c1 != c2) { c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); if (c1 != c2) { c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); if (c1 != c2) { return c1 - c2; } } } } return n1 - n2; } } }