/*******************************************************************************
* Copyright (c) 2009, 2010 Innovation Gate GmbH.
* 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:
* Innovation Gate GmbH - initial API and implementation
******************************************************************************/
package de.innovationgate.eclipse.editors.tml;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.FindReplaceDocumentAdapter;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import de.innovationgate.eclipse.editors.Plugin;
import de.innovationgate.eclipse.editors.config.TMLTag;
import de.innovationgate.eclipse.editors.config.TMLTagAttribute;
import de.innovationgate.eclipse.editors.config.TMLTagDefinitions;
import de.innovationgate.eclipse.editors.helpers.CompletionProposalComparator;
import de.innovationgate.eclipse.editors.helpers.ContextInformation;
import de.innovationgate.eclipse.editors.helpers.TMLReferenceLookup;
import de.innovationgate.eclipse.editors.tmlscript.parsing.TMLScriptRegion;
import de.innovationgate.eclipse.utils.wga.WGADesignStructureHelper;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.wga.common.beans.csconfig.v1.Version;
import de.innovationgate.wga.model.VersionCompliance;
import de.innovationgate.wga.model.WGADesignConfigurationModel;
/**
* represent a tml start tag region
* and offers CompletionProposals
*
*
*/
public class TMLRegion {
private static final String TML_REGION_PREFIX = "<tml:";
private IRegion _documentRegion;
private IDocument _document;
private int _cursorAbsolute;
private int _cursorRelative;
private String _tagName;
private Map<String, String> _attributes = new HashMap<String, String>();
/**
* contains attributes with no values (without trailing <code>=""</code>)
* useful for tag validation
*/
private Set<String> _valuelessAttributes = new HashSet<String>();
private String _content = "";
private TMLRegion(IRegion region, IDocument document, int cursorPositionInDocument) {
_documentRegion = region;
_document = document;
_cursorAbsolute = cursorPositionInDocument;
}
public static final TMLRegion parse(IRegion region, IDocument document, int cursorPositionInDocument) throws ParseException {
TMLRegion tmlRegion = new TMLRegion(region, document, cursorPositionInDocument);
tmlRegion.parse();
return tmlRegion;
}
public static final TMLRegion parse(IRegion region, IDocument document) throws ParseException {
return parse(region, document, region.getOffset());
}
private void parse() throws ParseException {
try {
// extract region content
try {
_content = _document.get(_documentRegion.getOffset(), _documentRegion.getLength());
// int cursorRelative = _cursorAbsolute - _region.getOffset();
//
// String beforeCursor = _content.substring(0, cursorRelative);
// int beforeContentLength = beforeCursor.length();
// beforeCursor = beforeCursor.replaceAll("\\n", "");
// beforeCursor = beforeCursor.replaceAll("\\t", "");
// // count whitespaces in prefix
// int i = 0;
// while (i < beforeCursor.length() && beforeCursor.charAt(i) == ' ') {
// i++;
// }
// int filteredCharsBeforeCursor = beforeContentLength - beforeCursor.length() + i;
_content = _content.replaceAll("\\n", " ");
_content = _content.replaceAll("\\t", " ");
//_content = _content.trim();
_cursorRelative = _cursorAbsolute - _documentRegion.getOffset(); //- filteredCharsBeforeCursor;
} catch (BadLocationException e) {
throw new ParseException("Unable to extract content of region.", _documentRegion.getOffset());
}
if (_content.startsWith("</tml:")) {
// stop tag - parse tagname only
_tagName = _content.substring(_content.indexOf("</tml:") + "</tml:".length(), _content.indexOf(">"));
_tagName = _tagName.trim();
} else if (_content.startsWith(TML_REGION_PREFIX)){
// perform start tag parsing
// parse tagname
String tagContent = _content.substring(_content.indexOf(":") + 1);
Set<String> tagNameStopWords = new HashSet<String>();
tagNameStopWords.add(" ");
tagNameStopWords.add("/>");
tagNameStopWords.add(">");
tagNameStopWords.add("\"");
tagNameStopWords.add("<");
//tagNameStopWords.add("\n");
int tagnameEndPos = searchForward(tagNameStopWords, tagContent);
if (tagnameEndPos == -1) {
tagnameEndPos = _cursorRelative - TML_REGION_PREFIX.length();
}
_tagName = tagContent.substring(0, tagnameEndPos).trim();
// parse attributes
int attributesStart = TML_REGION_PREFIX.length() + _tagName.length();
int attributesEnd = _content.length();
if (_content.endsWith("/>")) {
attributesEnd = attributesEnd - 2;
} else if (_content.endsWith(">")) {
attributesEnd = attributesEnd - 1;
}
String attributes = _content.substring(attributesStart, attributesEnd);
attributes = attributes.replaceAll("\n", "");
attributes = attributes.replaceAll("\r", "");
if (attributes.contains("<tml:")) {
// attribute section might contain <tml: start sequence if current tag partion is not closed yet and current tag is followed by another tml tag
// remove this
attributes = attributes.substring(0, attributes.indexOf("<tml:"));
}
attributes = attributes.trim();
if (attributes.length() > 0) {
attributes = WGUtils.strReplace(attributes, "\\\"", "$IG_APO$", true);
attributes = WGUtils.strReplace(attributes, "\"\"", "\"$IG_EMPTY$\"", true);
String[] attributeTokens = attributes.split("\"*\"");
for (int i=0; i < attributeTokens.length; i += 2) {
String key = attributeTokens[i];
boolean valueless = false;
if (!key.contains("=")) {
valueless = true;
}
key = key.replaceAll("=", "");
String value = null;
if (key.contains(" ")) {
String[] keys = key.split(" ");
key = keys[keys.length -1];
key = key.trim();
// all other strings in key array are attributes without = and value - add to attribute list
for (int j=0; j < keys.length - 1; j++) {
String sValueless = keys[j].trim();
if (sValueless.length() > 0) {
_valuelessAttributes.add(sValueless.toLowerCase());
}
}
}
if ((i + 1) < attributeTokens.length) {
value = attributeTokens[i + 1];
}
if (!valueless) {
value = WGUtils.strReplace(value, "$IG_APO$", "\\\"", true);
value = WGUtils.strReplace(value, "$IG_EMPTY$","", true);
_attributes.put(key.toLowerCase(), value);
} else {
_valuelessAttributes.add(key.toLowerCase());
}
}
}
}
} catch (ParseException e) {
throw e;
} catch (Exception e) {
throw new ParseException("Unknown parsing error '" + e.getMessage() + "'", -1);
}
}
public boolean hasAttribute(String name) {
return _attributes.containsKey(name.toLowerCase());
}
public boolean hasAttribute(String name, String value) {
if (isDynamicAttributeValue(name)) {
return false;
} else {
String attValue = getAttributeValue(name);
if (attValue != null) {
return attValue.equals(value);
}
}
return false;
}
public String getAttributeValue(String name) {
return _attributes.get(name.toLowerCase());
}
public Set<String> getAttributeNames() {
return _attributes.keySet();
}
private static int searchForward(Set<String> stopwords, String input) {
int lowestIndex = Integer.MAX_VALUE;
Iterator<String> stopIt = stopwords.iterator();
while (stopIt.hasNext()) {
String stopWord = stopIt.next();
int stop = input.indexOf(stopWord);
if (stop != -1) {
if (stop < lowestIndex) {
lowestIndex = stop;
}
}
}
if (lowestIndex == Integer.MAX_VALUE) {
return -1;
} else {
return lowestIndex;
}
}
public ICompletionProposal[] createProposals(ITextViewer viewer, IFile activeFile) {
List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
if (isCursorInTagName()) {
// create tag name proposals
String alreadyTyped = getTagName();
if (alreadyTyped != null) {
if (!alreadyTyped.startsWith("[")) {
Iterator<TMLTag> it = TMLTagDefinitions.getInstance(WGADesignStructureHelper.getWGAVersionCompliance(activeFile)).getTagsByPrefix(alreadyTyped).iterator();
while (it.hasNext()) {
TMLTag tag = it.next();
proposals.addAll(createTagProposals(alreadyTyped, tag, _document, _cursorAbsolute,activeFile));
}
} else {
Version wgaVersion = (Version) WGADesignConfigurationModel.VERSIONCOMPLIANCE_TO_WGA_VERSION.get(WGADesignStructureHelper.getWGAVersionCompliance(activeFile).getKey());
if (wgaVersion.isAtLeast(4, 1)) {
proposals.addAll(createIncludeModuleProposals(alreadyTyped, _document, _cursorAbsolute, activeFile));
}
}
}
} else if (!isCursorInAttributeValue()) {
// create attribute name proposals
String search = _content.substring(0, _cursorRelative);
int attributePrefixStartPos = searchBackwards(" ", search, "<");
if (attributePrefixStartPos != -1) {
attributePrefixStartPos += 1;
String typed = search.substring(attributePrefixStartPos, _cursorRelative);
TMLTag tag = TMLTagDefinitions.getInstance(WGADesignStructureHelper.getWGAVersionCompliance(activeFile)).getTagByName(getTagName());
if (tag != null) {
Iterator<TMLTagAttribute> it = tag.getAttributesByPrefix(typed).iterator();
while (it.hasNext()) {
TMLTagAttribute attrib = it.next();
if (!_attributes.containsKey(attrib.getName()) && !attrib.isDeprecated()) {
String attName = attrib.getName();
String display = attName;
StringBuffer replacement = new StringBuffer();
replacement.append(attName);
replacement.append("=\"\"");
int cursorPosAfterReplace = replacement.toString().length() - 1;
ContextInformation contextInfo = Plugin.getDefault().getContextInformationProvider().getAttributeInformation(tag.getName(), attrib.getName(), activeFile);
proposals.add(new TMLCompletionProposal(replacement.toString(), _cursorAbsolute - typed.length(), typed.length(), cursorPosAfterReplace, null, display, contextInfo));
}
}
}
}
} else if (getAttributeNameOfValueCursorPosition() != null) {
// create attribute value proposals
String attribute = getAttributeNameOfValueCursorPosition();
String search = _content.substring(0, _cursorRelative);
int attributeValueStartPos = searchBackwards("\"", search, "=");
String typed = "";
if (attributeValueStartPos != -1) {
typed = _content.substring(attributeValueStartPos + 1, _cursorRelative);
} else {
typed = "";
}
TMLTag tag = TMLTagDefinitions.getInstance(WGADesignStructureHelper.getWGAVersionCompliance(activeFile)).getTagByName(getTagName());
if (tag != null) {
TMLTagAttribute tagAttribute = tag.getAttribute(attribute);
if (tagAttribute != null) {
if (tagAttribute.isTmlscriptExpression()) {
// perform tmlscript code completion
try {
IRegion region = getAttributeValueRegion(attribute);
TMLScriptRegion tmlScriptRegion = TMLScriptRegion.parse(region, _document, _cursorAbsolute, WGADesignStructureHelper.getWGAVersionCompliance(activeFile));
proposals.addAll(tmlScriptRegion.createProposals());
}
catch (BadLocationException e) {
}
} else {
// lookup attribute values
Iterator<String> values = tag.getAttributeValuesByPrefix(attribute, typed, _document, this, viewer, activeFile).iterator();
while (values.hasNext()) {
String value = values.next();
String display = value;
StringBuffer replacement = new StringBuffer();
replacement.append(value);
int cursorPosAfterReplace = replacement.toString().length() + 1;
ContextInformation info = Plugin.getDefault().getContextInformationProvider().getAttributeInformation(tag.getName(), attribute, value,activeFile);
proposals.add(new TMLCompletionProposal(replacement.toString(), _cursorAbsolute - typed.length(), typed.length(), cursorPosAfterReplace, null, display, info));
}
}
}
}
}
try {
if (!isCursorInAttributeValue()) {
proposals.addAll(computeCloseTagProposals(_document, _cursorAbsolute));
}
} catch (BadLocationException e) {
}
Collections.sort(proposals, new CompletionProposalComparator());
return proposals.toArray(new ICompletionProposal[0]);
}
private List<ICompletionProposal> createIncludeModuleProposals(String alreadyTyped, IDocument document, int cursorAbsolute, IFile activeFile) {
List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
Iterator<String> modules = TMLReferenceLookup.lookupValues(this, activeFile).iterator();
while (modules.hasNext()) {
String module = modules.next();
if (module.startsWith(alreadyTyped.substring(1))) {
String display = "<tml:[" + module + "]>";
StringBuffer replacement = new StringBuffer();
replacement.append("[" + module + "]");
int cursorPosAfterReplace = replacement.length();
replacement.append(">");
proposals.add(new CompletionProposal(replacement.toString(), cursorAbsolute - alreadyTyped.length(), alreadyTyped.length(), cursorPosAfterReplace, null, display, null, null));
}
}
return proposals;
}
private List<ICompletionProposal> createTagProposals(String alreadyTyped, TMLTag tag, IDocument document, int documentOffset, IFile file) {
List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
if (tag != null && !tag.isDeprecated()) {
// String tagDescription = null;
ContextInformation info = Plugin.getDefault().getContextInformationProvider().getTagInformation(tag.getName(), file);
// if (info != null) {
// tagDescription = info.getInformation();
// }
//String display = "<tml:" + tagName + "/>";
// generate attribute section
StringBuffer attributeSection = new StringBuffer();
int offsetFirstAttribute = generateAttributeSection(tag, attributeSection);
// if (tag.isBodyNeeded() || tag.isBodyRecommended()) {
// // create proposal with body
// String display = "<tml:" + tag.getName() + " " + attributeSection + "> ... </tml:" + tag.getName() + ">";
// StringBuffer replacement = new StringBuffer();
// replacement.append(tag.getName());
// if (tag.isAttributeNecessary()) {
// replacement.append(" ");
// }
// int cursorPosAfterReplace = replacement.length();
// if (offsetFirstAttribute != -1) {
// // append attribute section
// replacement.append(" ");
// replacement.append(attributeSection);
// cursorPosAfterReplace += offsetFirstAttribute + 1;
// }
// replacement.append(">");
// if (tag.isMultiLineBodyRecommended()) {
// replacement.append(computeIndent("\n\t", documentOffset, document));
// }
// if (offsetFirstAttribute == -1 && !tag.isAttributeNecessary()) {
// // no attributes - place cursor in body
// cursorPosAfterReplace = replacement.length();
// }
// if (tag.isMultiLineBodyRecommended()) {
// replacement.append(computeIndent("\n", documentOffset, document));
// }
// replacement.append("</tml:" + tag.getName() + ">");
// proposals.add(new CompletionProposal(replacement.toString(), documentOffset - alreadyTyped.length(), alreadyTyped.length(), cursorPosAfterReplace, null, display, null, tagDescription));
// } else if (!tag.isBodyNeeded()) {
String display = null;
if (offsetFirstAttribute != -1) {
display = "<tml:" + tag.getName() + " " + attributeSection; // + ">";
//} else if (tag.isAttributeNecessary()) {
//display = "<tml:" + tag.getName() + " >";
} else {
display = "<tml:" + tag.getName(); // + ">";
}
StringBuffer replacement = new StringBuffer();
replacement.append(tag.getName());
//if (tag.isAttributeNecessary()) {
// replacement.append(" ");
//}
int cursorPosAfterReplace = replacement.length();
if (offsetFirstAttribute != -1) {
// append attribute section
replacement.append(" ");
replacement.append(attributeSection);
cursorPosAfterReplace += offsetFirstAttribute + 1;
}// else if (!tag.isAttributeNecessary()) {
//cursorPosAfterReplace = replacement.length() + 1;
//}
//replacement.append(">");
proposals.add(new TMLCompletionProposal(replacement.toString(), documentOffset - alreadyTyped.length(), alreadyTyped.length(), cursorPosAfterReplace, null, display, info));
// }
}
return proposals;
}
/**
* attribute section will be appended to the given stringbuffer
* @param tag
* @param section
* @return offset of first attribute value in section or -1 if no attributes generated
*/
private int generateAttributeSection(TMLTag tag, StringBuffer section) {
Iterator<TMLTagAttribute> attribs = tag.getRequiredAttributes().iterator();
boolean firstAttribute = true;
int cursorPosFirstAttribute = -1;
while (attribs.hasNext()) {
TMLTagAttribute attribute = attribs.next();
section.append(attribute.getName());
section.append("=\"\"");
if (firstAttribute) {
firstAttribute = false;
cursorPosFirstAttribute = section.toString().length() - 1;
}
if (attribs.hasNext()) {
section.append(" ");
}
}
return cursorPosFirstAttribute;
}
/**
* checks if the cursor is on tagname
* cursor behind "<tml:", and next char is an ' ', ', ", EOF or LineDelimiter
* @return
*/
public boolean isCursorInTagName() {
List<String> lineDelimiters = Arrays.asList(_document.getLegalLineDelimiters());
String search = _content.substring(0, _cursorRelative);
if (search.startsWith("<tml:") && search.indexOf(" ") == -1) {
if (_cursorRelative < _content.length()) {
char nextChar = _content.charAt(_cursorRelative);
if (nextChar == ' ' || lineDelimiters.contains(Character.toString(nextChar)) || nextChar == '\'' || nextChar == '"' || nextChar == '<') {
return true;
} else {
return false;
}
} else {
return true;
}
}
return false;
}
/**
* checks if the cursor is in an attribute value
* @return
*/
public boolean isCursorInAttributeValue() {
char[] search = _content.substring(0, _cursorRelative).toCharArray();
// count unescaped '"'
int count = 0;
for (int i=search.length - 1; i >= 0; i--) {
if (search[i] == '"') {
if (i-1 >= 0) {
if (search[i-1] != '\\') {
count++;
}
} else {
count++;
}
}
}
return (count%2 != 0);
}
/**
* returns the attribute name if the cursor is in the attribute value section
* null otherwise
* @return
*/
private String getAttributeNameOfValueCursorPosition() {
String contentBeforeCursor = _content.substring(0, _cursorRelative).trim();
String contentBehindCursor = _content.substring(_cursorRelative).trim();
if (contentBehindCursor.startsWith("\"")) {
int attribEnd = searchBackwards("=\"", contentBeforeCursor, " ");
if (attribEnd != -1) {
String attribute = contentBeforeCursor.substring(0, attribEnd);
int attribStart = attribute.lastIndexOf(" ");
if (attribStart != -1) {
attribute = attribute.substring(attribStart).trim();
return attribute;
}
}
}
return null;
}
/**
* returns the attribute name on which the cursor is placed
* null if cursor is not in an attribute section
* @return
*/
public String getAttributeAtCursor() {
int cursorPos = _cursorRelative;
if (isCursorInAttributeValue()) {
String search = _content.substring(0, cursorPos);
for (int i = search.length() - 1; i > 0; i--) {
char c = search.charAt(i);
if (c == '"') {
break;
} else {
cursorPos--;
}
}
if (cursorPos > 1) {
cursorPos = cursorPos-2;
}
}
String contentBeforeCursor = _content.substring(0, cursorPos);
String contentBehindCursor = _content.substring(cursorPos);
int attributeStartPos = searchBackwards(" ", contentBeforeCursor, "<");
if (attributeStartPos != -1) {
attributeStartPos += 1;
int attributeEndPos = searchForward("=", contentBehindCursor, ">");
if (attributeEndPos != -1) {
String name = contentBeforeCursor.substring(attributeStartPos);
name += contentBehindCursor.substring(0, attributeEndPos);
if (name.trim().equals("")) {
return null;
} else {
return name;
}
}
}
return null;
}
private boolean isCursorBetween(String wordBefore, String wordBehind, String stopWordBefore, String stopWordBehind) {
String contentBehindCursor = _content.substring(_cursorRelative);
int posWordBehind = searchForward(wordBehind, contentBehindCursor, stopWordBehind);
String contentBeforeCursor = _content.substring(0, _cursorRelative);
int posWordBefore = searchBackwards(wordBefore, contentBeforeCursor, stopWordBefore);
if (posWordBefore != -1 && posWordBehind != -1) {
return true;
} else {
return false;
}
}
private static int searchBackwards(String search, String input, String stopAt) {
for (int i=input.length() - 1; i >= 0; i--) {
if (input.startsWith(search, i)) {
return i;
} else {
if (stopAt != null) {
if (input.startsWith(stopAt, i)) {
return -1;
}
}
}
}
return -1;
}
private static int searchForward(String search, String input, String stopAt) {
int start = input.indexOf(search);
if (start != -1 && stopAt != null) {
int stop = input.indexOf(stopAt);
if (stop != -1 && stop < start) {
return -1;
}
}
return start;
}
public String getTagName() {
return _tagName;
}
public boolean isClosed() {
return _content.trim().endsWith("/>");
}
public static List<ICompletionProposal> computeCloseTagProposals(IDocument document, int offset) throws BadLocationException {
List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
IRegion region = document.getPartition(offset);
// fallback to current edit line if partition has length 0 - for e.g. end of document
if (region.getLength() == 0) {
region = document.getLineInformationOfOffset(offset);
}
// search backwards for a tmltag with body which might need to be closed
int pos = offset - 1;
while (pos >= 0) {
ITypedRegion previousRegion = document.getPartition(pos);
if (previousRegion.getType().equals(TMLPartitionScanner.TML_TAG_START) && !isTMLPartitionClosed(previousRegion, document)) {
String alreadyTyped = document.get(region.getOffset(), offset-region.getOffset());
if (alreadyTyped.contains("\n")) {
alreadyTyped = alreadyTyped.substring(alreadyTyped.lastIndexOf("\n"));
}
int posEnd = alreadyTyped.lastIndexOf(">");
if (posEnd != -1) {
alreadyTyped = alreadyTyped.substring(posEnd + 1);
}
posEnd = alreadyTyped.lastIndexOf("<");
if (posEnd != -1) {
alreadyTyped = alreadyTyped.substring(posEnd);
} else {
alreadyTyped = "";
}
String tagName = TMLPartitionScanner.determineTMLTagName(previousRegion, document);
if (document.getLineOfOffset(offset) == document.getLineOfOffset(region.getOffset())) {
// completion in same line
int start = previousRegion.getOffset() + previousRegion.getLength();
int end = offset;
String typedBody = document.get(start, end - start);
if (typedBody.trim().equals("")) {
// create multi line completion - bc. empty tagbody makes no sense
String display = "... </tml:" + tagName + ">";
StringBuffer replacement = new StringBuffer("\n");
replacement.append(computeIndent("\t", offset, document));
int cursorPosAfterReplace = replacement.length();
replacement.append(computeIndent("\n", offset, document));
replacement.append("</tml:" + tagName + ">");
if (replacement.toString().startsWith(alreadyTyped.trim())) {
int completionStartPos = alreadyTyped.lastIndexOf("<");
if (completionStartPos == -1) {
completionStartPos = offset;
} else {
completionStartPos = offset - (alreadyTyped.length() - completionStartPos);
}
proposals.add(new CompletionProposal(replacement.toString(), completionStartPos, offset - completionStartPos, cursorPosAfterReplace, null, display, null, null));
}
// create close current tag completionv
display = "/>";
replacement = new StringBuffer("/>");
cursorPosAfterReplace = replacement.length();
if (replacement.toString().startsWith(alreadyTyped.trim())) {
int completionStartPos = previousRegion.getOffset() + previousRegion.getLength() + 1 - replacement.length();
proposals.add(new CompletionProposal(replacement.toString(), completionStartPos, offset - completionStartPos, cursorPosAfterReplace, null, display, null, null));
}
break;
} else {
String replacement = "</tml:" + tagName + ">";
if (replacement.startsWith(alreadyTyped.trim())) {
int completionStartPos = alreadyTyped.lastIndexOf("<");
if (completionStartPos == -1) {
completionStartPos = offset;
} else {
completionStartPos = offset - (alreadyTyped.length() - completionStartPos);
}
proposals.add(new CompletionProposal(replacement, completionStartPos, offset - completionStartPos, replacement.length()));
break;
} else {
break;
}
}
} else {
// completion in following line
String replacement = "</tml:" + tagName + ">";
if (replacement.startsWith(alreadyTyped.trim())) {
int completionStartPos = alreadyTyped.lastIndexOf("<");
if (completionStartPos == -1) {
completionStartPos = offset;
} else {
completionStartPos = offset - (alreadyTyped.length() - completionStartPos);
}
proposals.add(new CompletionProposal(replacement, completionStartPos, offset - completionStartPos, replacement.length()));
break;
} else {
break;
}
}
}
pos = previousRegion.getOffset() - 1;
}
return proposals;
//return null;
}
public static boolean isTMLPartitionClosed(ITypedRegion region, IDocument document) throws BadLocationException {
if (region.getType().equals(TMLPartitionScanner.TML_TAG_START)) {
String content = document.get(region.getOffset(), region.getLength());
if (content.trim().endsWith("/>")) {
return true;
}
String tagName = TMLPartitionScanner.determineTMLTagName(region, document);
if (tagName != null) {
// retrieve document partitions from region offset to document end
ITypedRegion[] partitions = document.computePartitioning(region.getOffset() + region.getLength(), document.getLength() - region.getOffset() - region.getLength());
int openTags = 1;
// search until end of doc or opentags == 0
for (int i = 0; i < partitions.length; i++) {
ITypedRegion partition = partitions[i];
if (partition.getType().equals(TMLPartitionScanner.TML_TAG_START)) {
content = document.get(partition.getOffset(), partition.getLength());
if (!content.trim().endsWith("/>")) {
String partitionTagName = TMLPartitionScanner.determineTMLTagName(partition, document);
if (tagName.equals(partitionTagName)) {
openTags++;
}
}
} else if (partition.getType().equals(TMLPartitionScanner.TML_TAG_STOP)) {
String partitionTagName = TMLPartitionScanner.determineTMLTagName(partition, document);
if (tagName.equals(partitionTagName)) {
openTags--;
}
}
if (openTags == 0) {
return true;
}
}
return openTags == 0;
} else {
return false;
}
} else if (region.getType().equals(TMLPartitionScanner.TML_TAG_STOP)) {
String tagName = TMLPartitionScanner.determineTMLTagName(region, document);
if (tagName != null) {
// retrieve document partitions from document beginn to region offset
ITypedRegion[] partitions = document.computePartitioning(0, region.getOffset());
int openTags = 1;
// search backwards until opentags == 0 or document start has been reached
for (int i = partitions.length - 1; i >= 0; i--) {
ITypedRegion partition = partitions[i];
if (partition.getType().equals(TMLPartitionScanner.TML_TAG_START)) {
String content = document.get(partition.getOffset(), partition.getLength());
if (!content.trim().endsWith("/>")) {
String partitionTagName = TMLPartitionScanner.determineTMLTagName(partition, document);
if (tagName.equals(partitionTagName)) {
openTags--;
}
}
} else if (partition.getType().equals(TMLPartitionScanner.TML_TAG_STOP)) {
String partitionTagName = TMLPartitionScanner.determineTMLTagName(partition, document);
if (tagName.equals(partitionTagName)) {
openTags++;
}
}
if (openTags == 0) {
return true;
}
}
return openTags == 0;
} else {
return false;
}
} else {
throw new IllegalArgumentException("Illegal partition type '" + region.getType() + "'.");
}
}
private static int findEndOfWhiteSpace(IDocument document, int offset, int end) throws BadLocationException {
while (offset < end) {
char c= document.getChar(offset);
if (c != ' ' && c != '\t') {
return offset;
}
offset++;
}
return end;
}
public static String computeIndent(String text, int offset, IDocument document) {
if (offset == -1 || document.getLength() == 0)
return text;
try {
// find start of line
int p= (offset == document.getLength() ? offset - 1 : offset);
IRegion info= document.getLineInformationOfOffset(p);
int start= info.getOffset();
// find white spaces
int end= findEndOfWhiteSpace(document, start, offset);
StringBuffer buf= new StringBuffer(text);
if (end > start) {
// append to input
buf.append(document.get(start, end - start));
}
return buf.toString();
} catch (BadLocationException excp) {
// stop work
}
return text;
}
public IRegion getDocumentRegion() {
return _documentRegion;
}
public void setDocumentRegion(IRegion documentRegion) {
_documentRegion = documentRegion;
}
public boolean isTagKnown(VersionCompliance versionCompliance) {
return TMLTagDefinitions.getInstance(versionCompliance).getTagByName(getTagName()) != null;
}
public Set<String> getValuelessAttributes() {
return _valuelessAttributes;
}
public String getContent() {
return _content;
}
public boolean isDynamicAttributeValue(String name) {
String value = getAttributeValue(name);
if (value == null) {
return false;
} else {
return (value.trim().startsWith("{") && value.trim().endsWith("}")) || (value.trim().startsWith("[") && value.trim().endsWith("]"));
}
}
public IRegion getAttributeValueRegion(String attributeName) throws BadLocationException {
FindReplaceDocumentAdapter findReplaceAdapter = new FindReplaceDocumentAdapter(_document);
IRegion region = findReplaceAdapter.find(_documentRegion.getOffset(), attributeName + "=\"" + getAttributeValue(attributeName) + "\"", true, true, false, false);
if (region != null) {
String tmp = attributeName + "=\"";
return new Region(region.getOffset() + tmp.length(), region.getLength() - tmp.length() - 1);
} else {
return null;
}
/*
IRegion end = findReplaceAdapter.find(start.getOffset() + start.getLength(), "\"", true, true, false, false);
boolean startPosInPartition = start != null && start.getOffset() > _documentRegion.getOffset();
boolean endPosInPartition = end != null && end.getOffset() < (_documentRegion.getOffset() + _documentRegion.getLength());
if (startPosInPartition && endPosInPartition) {
int startPos = start.getOffset() + attributeName.length() + "=\"".length();
if (startPos < end.getOffset()) {
new Region(startPos, end.getOffset()-startPos);
}
}
return null;*/
}
}