/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.syncope.ide.eclipse.plugin.editors.htmlhelpers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Stack;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ContextInformation;
import org.eclipse.jface.text.contentassist.ContextInformationValidator;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
public class HTMLCompletionProcessor extends HTMLTemplateAssistProcessor {
private int offset;
private boolean xhtmlMode = false;
private char[] chars = {};
private boolean assistCloseTag = true;
protected String[] getLastWord(final String text) {
StringBuilder sb = new StringBuilder();
Stack<String> stack = new Stack<String>();
String word = "";
String prevTag = "";
String lastTag = "";
String attr = "";
String temp1 = ""; // temporary
String temp2 = ""; // temporary
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
// skip scriptlet
if (c == '<' && text.length() > i + 1 && text.charAt(i + 1) == '%') {
i = text.indexOf("%>", i + 2);
if (i == -1) {
i = text.length();
}
continue;
}
// skip XML declaration
if (c == '<' && text.length() > i + 1 && text.charAt(i + 1) == '?') {
i = text.indexOf("?>", i + 2);
if (i == -1) {
i = text.length();
}
continue;
}
if (isDelimiter(c)) {
temp1 = sb.toString();
// skip whitespaces in the attribute value
if (temp1.length() > 1
&& ((temp1.startsWith("\"") && !temp1.endsWith("\"") && c != '"')
|| (temp1.startsWith("'") && !temp1.endsWith("'") && c != '\''))) {
sb.append(c);
continue;
}
if (temp1.length() == 1 && ((temp1.equals("\"") || (temp1.equals("'"))))) {
sb.append(c);
continue;
}
if (!temp1.equals("")) {
temp2 = temp1;
if (temp2.endsWith("=") && !prevTag.equals("") && !temp2.equals("=")) {
attr = temp2.substring(0, temp2.length() - 1);
}
}
if (temp1.startsWith("<") && !temp1.startsWith("</") && !temp1.startsWith("<!")) {
prevTag = temp1.substring(1);
if (!temp1.endsWith("/")) {
stack.push(prevTag);
}
} else if (temp1.startsWith("</") && stack.size() != 0) {
stack.pop();
} else if ((!temp1.startsWith("\"") && !temp1.startsWith("'"))
&& temp1.endsWith("/") && stack.size() != 0) {
stack.pop();
}
sb.setLength(0);
if (c == '<') {
sb.append(c);
} else if (c == '"' || c == '\'') {
if (temp1.startsWith("\"") || temp1.startsWith("'")) {
sb.append(temp1);
}
sb.append(c);
} else if (c == '>') {
prevTag = "";
attr = "";
}
} else {
if (c == '=' && !prevTag.equals("")) {
attr = temp2.trim();
}
temp1 = sb.toString();
if (temp1.length() > 1
&& (temp1.startsWith("\"") && temp1.endsWith("\""))
|| (temp1.startsWith("'") && temp1.endsWith("'"))) {
sb.setLength(0);
}
sb.append(c);
}
}
if (stack.size() != 0) {
lastTag = (String) stack.pop();
}
// Hmm... it's not perfect...
if (attr.endsWith("=")) {
attr = attr.substring(0, attr.length() - 1);
}
word = sb.toString();
return new String[]{word, prevTag, lastTag, attr};
}
//Tests a character is delimiter or not delimiter.
protected boolean isDelimiter(final char c) {
return (c == ' ' || c == '(' || c == ')' || c == ',' //|| c == '.'
|| c == ';' || c == '\n' || c == '\r' || c == '\t' || c == '+'
|| c == '>' || c == '<' || c == '*' || c == '^' //|| c == '{'
//|| c == '}'
|| c == '[' || c == ']' || c == '"' || c == '\'');
}
protected List<TagInfo> getTagList() {
return TagDefinition.getTagInfoAsList();
}
protected TagInfo getTagInfo(final String name) {
List<TagInfo> tagList = TagDefinition.getTagInfoAsList();
for (int i = 0; i < tagList.size(); i++) {
TagInfo info = (TagInfo) tagList.get(i);
if (info.getTagName().equals(name)) {
return info;
}
}
return null;
}
public ICompletionProposal[] computeCompletionProposals(final ITextViewer viewer,
final int documentOffset) {
String text = viewer.getDocument().get().substring(0, documentOffset);
String[] dim = getLastWord(text);
String word = dim[0].toLowerCase();
String prev = dim[1].toLowerCase();
String last = dim[2];
this.offset = documentOffset;
List<ICompletionProposal> list = new ArrayList<ICompletionProposal>();
List<TagInfo> tagList = getTagList();
// attribute value
if (word.startsWith("<") && !word.equals("</")) {
TagInfo parent = getTagInfo(last);
//tagList = new ArrayList < TagInfo>();
if (parent != null) {
String[] childNames = parent.getChildTagNames();
for (int i = 0; i < childNames.length; i++) {
tagList.add(getTagInfo(childNames[i]));
}
}
for (int i = 0; i < tagList.size(); i++) {
TagInfo tagInfo = (TagInfo) tagList.get(i);
if (tagInfo instanceof TextInfo) {
TextInfo textInfo = (TextInfo) tagInfo;
if ((textInfo.getText().toLowerCase()).indexOf(word) == 0) {
list.add(new CompletionProposal(
textInfo.getText(), documentOffset - word.length(),
word.length(), textInfo.getPosition()));
}
continue;
}
String tagName = tagInfo.getTagName();
if (("<" + tagInfo.getTagName().toLowerCase()).indexOf(word) == 0) {
String assistKeyword = tagName;
int position = 0;
// required attributes
AttributeInfo[] requierAttrs = tagInfo.getRequiredAttributeInfo();
for (int j = 0; j < requierAttrs.length; j++) {
assistKeyword = assistKeyword + " " + requierAttrs[j].getAttributeName();
if (requierAttrs[j].hasValue()) {
assistKeyword = assistKeyword + "=\"\"";
if (j == 0) {
position = tagName.length() + requierAttrs[j].getAttributeName().length() + 3;
}
}
}
if (tagInfo.hasBody()) {
assistKeyword = assistKeyword + ">";
if (true) {
if (position == 0) {
position = assistKeyword.length();
}
assistKeyword = assistKeyword + "</" + tagName + ">";
}
} else {
if (tagInfo.isEmptyTag() && !xhtmlMode) {
assistKeyword += ">";
} else {
assistKeyword += "/>";
}
}
if (position == 0) {
position = assistKeyword.length();
}
try {
list.add(new CompletionProposal(
assistKeyword, documentOffset - word.length() + 1,
word.length() - 1, position));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
// attribute
} else if (!prev.equals("")) {
String tagName = prev;
TagInfo tagInfo = getTagInfo(tagName);
if (tagInfo != null) {
AttributeInfo[] attrList = tagInfo.getAttributeInfo();
for (int j = 0; j < attrList.length; j++) {
if (attrList[j].getAttributeName().toLowerCase().indexOf(word) == 0) {
String assistKeyword = null;
int position = 0;
if (attrList[j].hasValue()) {
assistKeyword = attrList[j].getAttributeName() + "=\"\"";
position = 2;
} else {
assistKeyword = attrList[j].getAttributeName();
position = 0;
}
list.add(new CompletionProposal(
assistKeyword, documentOffset - word.length(), word.length(),
attrList[j].getAttributeName().length() + position));
}
}
}
// close tag
} else if (!last.equals("")) {
TagInfo info = getTagInfo(last);
if (info == null || xhtmlMode || info.hasBody() || !info.isEmptyTag()) {
String assistKeyword = "</" + last + ">";
int length = 0;
if (word.equals("</")) {
length = 2;
}
String contentBefore = viewer.getDocument().get().substring(0, documentOffset - length);
if (contentBefore.endsWith("\t")) {
list.add(new CompletionProposal(
assistKeyword, documentOffset - (length + 1), length + 1,
assistKeyword.length()));
} else {
list.add(new CompletionProposal(
assistKeyword, documentOffset - length, length,
assistKeyword.length()));
}
}
}
sortCompilationProposal(list);
ICompletionProposal[] templates = super.computeCompletionProposals(viewer, documentOffset);
for (int i = 0; i < templates.length; i++) {
list.add(templates[i]);
}
ICompletionProposal[] prop = list.toArray(new ICompletionProposal[list.size()]);
return prop;
}
@Override public IContextInformation[] computeContextInformation(final ITextViewer viewer,
final int documentOffset) {
ContextInformation[] info = new ContextInformation[0];
return info;
}
@Override public char[] getCompletionProposalAutoActivationCharacters() {
return chars;
}
@Override public char[] getContextInformationAutoActivationCharacters() {
return chars;
}
@Override public IContextInformationValidator getContextInformationValidator() {
return new ContextInformationValidator(this);
}
@Override public String getErrorMessage() {
return "Error";
}
public static void sortCompilationProposal(final List<ICompletionProposal> prop) {
Collections.sort(prop, new Comparator<ICompletionProposal>() {
public int compare(final ICompletionProposal o1, final ICompletionProposal o2) {
return o1.getDisplayString().compareTo(o2.getDisplayString());
}
});
}
public void setAutoAssistChars(final char[] chars) {
if (chars != null) {
this.chars = chars;
}
}
public void setAssistCloseTag(final boolean assistCloseTag) {
this.assistCloseTag = assistCloseTag;
}
}