/*
* JBoss by Red Hat
* Copyright 2006-2009, Red Hat Middleware, LLC, and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ide.eclipse.freemarker.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.source.ISourceViewer;
import org.jboss.ide.eclipse.freemarker.Plugin;
public abstract class AbstractItem implements Item {
private ITypedRegion region;
private ISourceViewer viewer;
private IResource resource;
private List subDirectives;
private Item parentItem;
private ItemSet itemSet;
public final void load(ITypedRegion region, ISourceViewer viewer, IResource resource) {
this.region = region;
this.viewer = viewer;
this.resource = resource;
try {
init(region, viewer, resource);
}
catch (Exception e) {
Plugin.log(e);
}
}
protected abstract void init (ITypedRegion region, ISourceViewer viewer, IResource resource) throws Exception;
public boolean isStartItem() {
return false;
}
public boolean isEndItem() {
return false;
}
public boolean relatesToItem(Item directive) {
return false;
}
public void relateItem(Item directive) {
if (null == relatedItemsArr)
relatedItemsArr = new ArrayList();
relatedItemsArr.add(directive);
}
public boolean isNestable() {
return (null != getContents() && !getContents().endsWith("/")); //$NON-NLS-1$
}
public ITypedRegion getRegion() {
return region;
}
public List getChildItems() {
if (null == subDirectives) {
subDirectives = new ArrayList(0);
}
return subDirectives;
}
public void addSubDirective(Item directive) {
getChildItems().add(directive);
directive.setParentItem(this);
}
public ISourceViewer getViewer() {
return viewer;
}
protected Item getRelatedItem() {
return null;
}
protected Item[] relatedItems;
protected List relatedItemsArr;
public Item[] getRelatedItems() {
if (null == relatedItems) {
if (null != relatedItemsArr) {
relatedItems = (Item[]) relatedItemsArr.toArray(new Item[relatedItemsArr.size()]);
}
else if (null == getRelatedItem()) {
relatedItems = new Item[0];
}
else {
relatedItems = new Item[] {getRelatedItem()};
}
}
return relatedItems;
}
private String contents;
public String getContents () {
if (null == contents) {
contents = getFullContents();
if (null != contents) contents = contents.trim();
}
return contents;
}
private ContentWithOffset standardSplit;
public ContentWithOffset splitContents (int offset) {
if (offset == -1 && null != standardSplit) return standardSplit;
String s = getFullContents();
if (null == s) {
return new ContentWithOffset(new String[0], -1, -1, -1, -1, -1, -1, false, false);
}
int actualIndex = 0;
int actualIndexOffset = 0;
int actualOffset = 0;
int indexOffset = 0;
int offsetCount = 0;
int totalOffsetCount = 0;
int spacesEncountered = 0;
int totalSpacesEncountered = 0;
int cursorPos = getCursorPosition(offset);
List arr = new ArrayList();
StringBuffer current = new StringBuffer();
Stack currentStack = new Stack();
boolean escape = false;
boolean doEscape = false;
boolean doAppend = true;
boolean encounteredSpace = false;
boolean nextCharSpace = false;
for (int i=0; i<s.length(); i++) {
encounteredSpace = false;
char c = s.charAt(i);
if (totalOffsetCount == cursorPos) {
actualIndex = arr.size();
actualOffset = totalOffsetCount;
indexOffset = offsetCount;
actualIndexOffset = offset - cursorPos - indexOffset;
if (c == ' ') nextCharSpace = true;
}
totalOffsetCount++;
if (c == ' ' || c == '=' || c == '\r' || c == '\n') {
// we're probably going to split here
if (current.length() != 0) {
if (currentStack.size() == 0) {
arr.add(current.toString());
current = new StringBuffer();
offsetCount = 0;
if (c == '=') {
arr.add("="); //$NON-NLS-1$
current = new StringBuffer();
}
else {
encounteredSpace = true;
spacesEncountered ++;
totalSpacesEncountered ++;
}
}
doAppend = false;
}
else {
// just continue
}
}
if (!escape) {
if (c == '\"') {
if (currentStack.size() > 0) {
if (currentStack.peek() == "\"") //$NON-NLS-1$
currentStack.pop();
else
currentStack.push("\""); //$NON-NLS-1$
}
else
currentStack.push("\""); //$NON-NLS-1$
}
else if (c == '(') {
currentStack.push("("); //$NON-NLS-1$
}
else if (c == ')') {
if (currentStack.size() > 0 && currentStack.peek().equals(")")) //$NON-NLS-1$
currentStack.pop();
}
else if (c == '{') {
currentStack.push("{"); //$NON-NLS-1$
}
else if (c == '}') {
if (currentStack.size() > 0 && currentStack.peek().equals("}")) //$NON-NLS-1$
currentStack.pop();
}
else if (c == '\\') {
doEscape = true;
}
else {
for (int j=0; j<getDescriptors().length; j++) {
if (c == getDescriptors()[j]) {
doAppend = false;
break;
}
}
}
}
if (doAppend) {
current.append(c);
offsetCount++;
}
escape = doEscape;
doEscape = false;
doAppend = true;
}
if (current.length() > 0) {
arr.add(current.toString());
if (totalOffsetCount == cursorPos) {
actualOffset = totalOffsetCount;
indexOffset = offsetCount;
actualIndexOffset = offset - cursorPos - indexOffset;
}
}
else if (arr.size() == 0) {
arr.add(""); //$NON-NLS-1$
}
if (totalOffsetCount == cursorPos) {
actualIndex = arr.size()-1;
actualOffset = totalOffsetCount;
indexOffset = offsetCount;
actualIndexOffset = offset - cursorPos - indexOffset;
}
ContentWithOffset contentWithOffset = new ContentWithOffset(
(String[]) arr.toArray(new String[arr.size()]),
actualIndex, actualIndexOffset, indexOffset, actualOffset, spacesEncountered,
totalSpacesEncountered, encounteredSpace, nextCharSpace);
if (offset == -1) standardSplit = contentWithOffset;
return contentWithOffset;
}
protected int getCursorPosition (int offset) {
return offset - getOffset();
}
public String[] splitContents () {
ContentWithOffset rtn = splitContents(-1);
return rtn.getContents();
}
public class ContentWithOffset {
private String[] contents;
private int index;
private int indexOffset;
private int offsetInIndex;
private int offset;
private int spacesEncountered;
private int totalSpacesEncountered;
private boolean wasLastCharSpace;
private boolean isNextCharSpace;
public ContentWithOffset (String[] contents, int index, int indexOffset, int offsetInIndex,
int offset, int spacesEncountered, int totalSpacesEncountered,
boolean wasLastCharSpace, boolean isNextCharSpace) {
this.contents = contents;
this.index = index;
this.offsetInIndex = offsetInIndex;
this.indexOffset = indexOffset;
this.offset = offset;
this.spacesEncountered = spacesEncountered;
this.totalSpacesEncountered = totalSpacesEncountered;
this.wasLastCharSpace = wasLastCharSpace;
this.isNextCharSpace = isNextCharSpace;
}
public String[] getContents() {
return contents;
}
public void setContents(String[] contents) {
this.contents = contents;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public int getOffset() {
return offset;
}
public void setOffset(int offset) {
this.offset = offset;
}
public int getOffsetInIndex() {
return offsetInIndex;
}
public int getSpacesEncountered() {
return spacesEncountered;
}
public int getTotalSpacesEncountered() {
return totalSpacesEncountered;
}
public boolean wasLastCharSpace() {
return wasLastCharSpace;
}
public boolean isNextCharSpace() {
return isNextCharSpace;
}
public int getIndexOffset() {
return indexOffset;
}
}
public Item getParentItem() {
return parentItem;
}
public void setParentItem(Item parentItem) {
this.parentItem = parentItem;
}
public Item getStartItem () {
return this;
}
public boolean equals(Object arg0) {
if (arg0 instanceof Item) {
return ((Item) arg0).getRegion().equals(getRegion());
}
else return false;
}
public int hashCode() {
return getRegion().hashCode();
}
private String treeDisplay;
public String getTreeDisplay() {
if (null == treeDisplay) {
treeDisplay = getContents();
if (null != treeDisplay && treeDisplay.endsWith("/")) //$NON-NLS-1$
treeDisplay = treeDisplay.substring(0, treeDisplay.length()-1);
}
return treeDisplay;
}
public String getTreeImage() {
return null;
}
public boolean isStartAndEndItem() {
return false;
}
public String getSplitValue (int index) {
String[] values = splitContents();
if (null != values && values.length > index)
return values[index];
else return null;
}
public ICompletionProposal[] getCompletionProposals(int offset, Map context) {
return null;
}
private static final char[] descriptorTokens = new char[]{'/','#','@','[',']','<','>'};
public char[] getDescriptors () {
return descriptorTokens;
}
public ItemSet getItemSet() {
return itemSet;
}
public void setItemSet(ItemSet itemSet) {
this.itemSet = itemSet;
}
public String getFullContents () {
try {
return viewer.getDocument().get(
region.getOffset(), region.getLength());
}
catch (BadLocationException e) {
return null;
}
}
public int getOffset () {
return getRegion().getOffset();
}
public int getLength () {
return getRegion().getLength();
}
String firstToken = null;
public String getFirstToken() {
if (null == firstToken) {
StringBuffer sb = new StringBuffer();
String content = getContents();
for (int i=0; i<content.length(); i++) {
char c = content.charAt(i);
if (c == '\"') return null;
else if (c == '?') {
firstToken = sb.toString();
break;
}
else if (c == ' ' || c == '(' || c == ')' && sb.length() > 0) {
firstToken = sb.toString();
break;
}
else sb.append(c);
}
}
return firstToken;
}
public IResource getResource() {
return resource;
}
public void setResource(IResource resource) {
this.resource = resource;
}
public void addToContext(Map context) {
}
public void removeFromContext(Map context) {
}
public Item getEndItem() {
return null;
}
public String getName() {
return getFirstToken();
}
}