/* * 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.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; import org.eclipse.core.resources.IResource; 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.model.interpolation.BuiltInFragment; import org.jboss.ide.eclipse.freemarker.model.interpolation.Fragment; import org.jboss.ide.eclipse.freemarker.model.interpolation.NameFragment; import org.jboss.ide.eclipse.freemarker.model.interpolation.NullFragment; import org.jboss.ide.eclipse.freemarker.model.interpolation.ParametersFragment; import org.jboss.ide.eclipse.freemarker.model.interpolation.StringFragment; public class Interpolation extends AbstractDirective { private List fragments; protected void init(ITypedRegion region, ISourceViewer viewer, IResource resource) throws Exception { } public String getTreeImage() { return "interpolation.png"; //$NON-NLS-1$ } public synchronized ICompletionProposal[] getCompletionProposals(int offset, Map context) { Item parent = getParentItem(); List parents = new ArrayList(); Item tempItem = getParentItem(); while (null != parent) { parents.add(0, parent); parent = parent.getParentItem(); if (null != parent) tempItem = parent; } if (null == tempItem) tempItem = this; Item[] items = getItemSet().getRootItems(); for (int i=0; i<items.length; i++) { Item item = items[i]; if (tempItem.equals(item)) break; if (item.isStartItem()) { item.addToContext(context); if (null != item.getEndItem() && item.getEndItem().getRegion().getOffset() < (offset-1)) item.removeFromContext(context); } else item.addToContext(context); } for (Iterator i=parents.iterator(); i.hasNext(); ) { Item item = (Item) i.next(); for (Iterator i2=item.getChildItems().iterator(); i2.hasNext(); ) { Item item2 = (Item) i2.next(); if (parents.contains(item2)) break; item2.addToContext(context); } item.addToContext(context); } initFragments(); // find the fragment matching the offset int subOffset = offset - getOffset() - 2; if (subOffset < 0) return null; Fragment fragment = null; for (Iterator i = fragments.iterator(); i.hasNext(); ) { Fragment fragmentSub = (Fragment) i.next(); if (fragmentSub.getOffset() <= subOffset) fragment = fragmentSub; else break; } if (null != fragment) { // find the parent class Class parentClass = null; for (Iterator i = fragments.iterator(); i.hasNext(); ) { Fragment fragmentSub = (Fragment) i.next(); if (fragmentSub.equals(fragment)) break; else parentClass = fragmentSub.getReturnClass(parentClass, fragments, context, getResource(), getResource().getProject()); } return fragment.getCompletionProposals( subOffset - fragment.getOffset(), offset, parentClass, fragments, getViewer(), context, getResource(), getResource().getProject()); } else if (getContents().length() == 0 && subOffset == 0) { return new NullFragment().getCompletionProposals( subOffset, offset, null, fragments, getViewer(), context, getResource(), getResource().getProject()); } else return null; } private synchronized void initFragments () { if (null != fragments) return; fragments = new ArrayList(); StringBuffer sb = new StringBuffer(); String contents = getFullContents(); contents = contents.substring(2, contents.length()-1); Stack stack = new Stack(); int offsetStart = 0; boolean inString = false; boolean inBuiltIn = false; boolean inNameFragment = false; boolean inParameters = false; boolean escape = false; boolean doEscape = false; int offset = getOffset(); for (int i=0; i<contents.length(); i++) { doEscape = false; char c = contents.charAt(i); if (Character.isLetterOrDigit(c) && !inString && !inBuiltIn && !inNameFragment && !inParameters) inNameFragment = true; if (inNameFragment) { if (c == '?') { fragments.add(new NameFragment(offsetStart, sb.toString())); offsetStart = i; sb.delete(0, sb.length()); inNameFragment = false; inBuiltIn = true; } else if (c == '(') { fragments.add(new NameFragment(offsetStart, sb.toString())); offsetStart = i; sb.delete(0, sb.length()); inNameFragment = false; inParameters = true; } else if (c == '.') { fragments.add(new NameFragment(offsetStart, sb.toString())); offsetStart = i; sb.delete(0, sb.length()); inNameFragment = true; sb.append(c); } else if (c == ')') { // for now, forget about the stack fragments.add(new NameFragment(offsetStart, sb.toString())); offsetStart = i+1; sb.delete(0, sb.length()); inNameFragment = false; } else sb.append(c); } else if (inBuiltIn) { if (c == '?') { fragments.add(new BuiltInFragment(offsetStart, sb.toString())); offsetStart = i; sb.delete(0, sb.length()); } else if (c == ')') { fragments.add(new BuiltInFragment(offsetStart, sb.toString())); offsetStart = i+1; sb.delete(0, sb.length()); inBuiltIn = false; } else if (c == '.') { fragments.add(new BuiltInFragment(offsetStart, sb.toString())); inNameFragment = true; offsetStart = i; sb.delete(0, sb.length()); inBuiltIn = false; sb.append(c); } else sb.append(c); } else if (inParameters) { if (inString) if (!escape && c == '\"') inString = false; if (!inString && c == ')') { fragments.add(new ParametersFragment(offsetStart, sb.toString())); offsetStart = i+1; sb.delete(0, sb.length()); } else sb.append(c); } else if (inString) { if (escape) sb.append(c); else if (c == '\"') { fragments.add(new StringFragment(offsetStart, sb.toString())); offsetStart = i+1; sb.delete(0, sb.length()); } else if (c == '\\') { doEscape = true; sb.append(c); } else sb.append(c); } else if (c == '.') { if (sb.length() > 0) { if (inBuiltIn) fragments.add(new BuiltInFragment(offsetStart, sb.toString())); else fragments.add(new NameFragment(offsetStart, sb.toString())); } inNameFragment = true; offsetStart = i; sb.delete(0, sb.length()); } else if (c == '?') { if (inBuiltIn) fragments.add(new BuiltInFragment(offsetStart, sb.toString())); else fragments.add(new NameFragment(offsetStart, sb.toString())); inBuiltIn = true; offsetStart = i; sb.delete(0, sb.length()); } else if (c == '(') { if (fragments.size() == 0) { // for now, forget about the stack } else { if (sb.length() > 0 && !inBuiltIn) fragments.add(new NameFragment(offsetStart, sb.toString())); inParameters = true; offsetStart = i; sb.delete(0, sb.length()); } } else if (c == '"') { if (sb.length() > 0) fragments.add(new NameFragment(offsetStart, sb.toString())); inString = true; offsetStart = i; sb.delete(0, sb.length()); } else sb.append(c); offset++; escape = doEscape; } if (sb.length() > 0 || inBuiltIn) { if (inBuiltIn) fragments.add(new BuiltInFragment(offsetStart, sb.toString())); else fragments.add(new NameFragment(offsetStart, sb.toString())); } } private ClassLoader getClassLoader () { return Thread.currentThread().getContextClassLoader(); } public boolean isNestable() { return false; } public Class getReturnClass (Map context) { initFragments(); Class returnClass = null; for (Iterator i=fragments.iterator(); i.hasNext(); ) { Fragment fragment = (Fragment) i.next(); returnClass = fragment.getReturnClass(returnClass, fragments, context, getResource(), getResource().getProject()); } return returnClass; } public Class getSingularReturnClass (Map context) { initFragments(); Class returnClass = null; for (Iterator i=fragments.iterator(); i.hasNext(); ) { Fragment fragment = (Fragment) i.next(); if (i.hasNext()) returnClass = fragment.getReturnClass(returnClass, fragments, context, getResource(), getResource().getProject()); else returnClass = fragment.getSingularReturnClass(returnClass, fragments, context, getResource(), getResource().getProject()); } return returnClass; } }