|
What this is
Other links
The source code/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.editor.ext.html; import java.util.*; import java.awt.Color; import javax.swing.text.JTextComponent; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; import org.netbeans.editor.*; import org.netbeans.editor.Settings; import org.netbeans.editor.SettingsChangeEvent; import org.netbeans.editor.SettingsChangeListener; import org.netbeans.editor.SettingsUtil; import org.netbeans.editor.Utilities; import org.netbeans.editor.ext.*; import org.netbeans.editor.ext.html.dtd.*; /** * HTML completion results finder * * @author Petr Nejedly * @version 1.00 */ public class HTMLCompletionQuery implements CompletionQuery { private static boolean lowerCase; /** Perform the query on the given component. The query usually * gets the component's document, the caret position and searches back * to examine surrounding context. Then it returns the result. * @param component the component to use in this query. * @param offset position in the component's document to which the query will * be performed. Usually it's a caret position. * @param support syntax-support that will be used during resolving of the query. * @return result of the query or null if there's no result. */ public CompletionQuery.Result query(JTextComponent component, int offset, SyntaxSupport support) { Class kitClass = Utilities.getKitClass(component); if (kitClass != null) { lowerCase = SettingsUtil.getBoolean(kitClass, HTMLSettingsNames.COMPLETION_LOWER_CASE, HTMLSettingsDefaults.defaultCompletionLowerCase); } BaseDocument doc = (BaseDocument)component.getDocument(); if( doc.getLength() == 0 ) return null; // nothing to examine HTMLSyntaxSupport sup = (HTMLSyntaxSupport)support.get(HTMLSyntaxSupport.class); if( sup == null ) return null;// No SyntaxSupport for us, no hint for user DTD dtd = sup.getDTD(); if( dtd == null ) return null; // We have no knowledge about the structure! try { TokenItem item = null; TokenItem prev = null; // are we inside token or between tokens boolean inside = false; item = sup.getTokenChain( offset, offset+1 ); if( item != null ) { // inside document prev = item.getPrevious(); // this part of the code is smartcase deciding if (prev != null){ TokenItem prevv = prev; String prevvImage = prevv.getImage(); int index = prevvImage.length() - 1; // is in the previous tag a letter? if (prevv != null && prevv.getTokenID().getNumericID() == HTMLTokenContext.TAG_ID && prevv.getTokenID().getNumericID() == HTMLTokenContext.ARGUMENT_ID){ while (index > -1 && !Character.isLetter(prevvImage.charAt(index))) index--; } else index = -1; // if not find first tag with a letter while (index == -1 && prevv != null){ while (prevv != null && ((prevv.getTokenID().getNumericID() != HTMLTokenContext.TAG_ID && prevv.getTokenID().getNumericID() != HTMLTokenContext.ARGUMENT_ID) || prevv.getImage().trim().equals(">"))){ // NOI18N prevv = prevv.getPrevious(); } if (prevv != null){ prevvImage = prevv.getImage(); index = 0; while (index < prevvImage.length() && !Character.isLetter(prevvImage.charAt(index))) index++; if (index == prevvImage.length()){ index = -1; prevv = prevv.getPrevious(); } } } // is there a previous tag with a letter? if (prevv != null && index != -1){ lowerCase = !Character.isUpperCase(prevvImage.charAt(index)); } else{ lowerCase = true; } } // end of smartcase deciding inside = item.getOffset() < offset; } else { // @ end of document prev = sup.getTokenChain( offset-1, offset ); //!!! } boolean begin = (prev == null && !inside); /* if( prev == null && !inside ) System.err.println( "Beginning of document, first token = " + item.getTokenID() ); else if( item == null ) System.err.println( "End of document, last token = " + prev.getTokenID() ); else if( ! inside ) System.err.println( "Between tokens " + prev.getTokenID() + " and " + item.getTokenID() ); else System.err.println( "Inside token " + item.getTokenID() ); */ if( begin ) return null; TokenID id = null; List l = null; int len = 1; int itemOffset = 0; String preText = null; if( inside ) { id = item.getTokenID(); preText = item.getImage().substring( 0, offset - item.getOffset() ); itemOffset = item.getOffset(); } else { id = prev.getTokenID(); preText = prev.getImage().substring( 0, offset - prev.getOffset() ); itemOffset = prev.getOffset(); } /* Here are completion finders, each have its own set of rules and source of results * They are now written just for testing rules, I will rewrite them to more compact * and faster, tree form, as soon as i'll have them all. */ /* Character reference finder */ if( (id == HTMLTokenContext.TEXT || id == HTMLTokenContext.VALUE) && preText.endsWith( "&" ) ) { // NOI18N l = translateCharRefs( offset-len, len, dtd.getCharRefList( "" ) ); } else if( id == HTMLTokenContext.CHARACTER ) { if( inside || !preText.endsWith( ";" ) ) { // NOI18N len = offset - itemOffset; l = translateCharRefs( offset-len, len, dtd.getCharRefList( preText.substring( 1 ) ) ); } /* Tag finder */ } else if( id == HTMLTokenContext.TEXT && preText.endsWith( "<" ) ) { // NOI18N // There will be lookup for possible StartTags, in SyntaxSupport // l = translateTags( offset-len, len, sup.getPossibleStartTags ( offset-len, "" ) ); l = translateTags( offset-len, len, dtd.getElementList( "" ) ); //System.err.println("l = " + l ); } else if( id == HTMLTokenContext.TAG && preText.startsWith( "<" ) && !preText.startsWith( " from the tag name if(preText.substring(1).equals(itemText)) { //now I have to look ahead to get know whether //there are some attributes or an end of the tag //define how far to look ahead int lookLenght = 10; //default - thought up if(offset + lookLenght > doc.getLength()) lookLenght = doc.getLength() - offset; TokenItem aheadChainToken = sup.getTokenChain( offset, offset+lookLenght ); //test if next token is a whitespace and the next a tag token or an attribute token if(aheadChainToken != null && aheadChainToken.getTokenID().getNumericID() == HTMLTokenContext.WS_ID) { aheadChainToken = aheadChainToken.getNext(); if(aheadChainToken != null && (aheadChainToken.getTokenID().getNumericID() == HTMLTokenContext.TAG_ID || aheadChainToken.getTokenID().getNumericID() == HTMLTokenContext.ARGUMENT_ID )) { //do not put the item into CC - otherwise it will break the completed tag l = null; } } } } /* EndTag finder */ } else if( id == HTMLTokenContext.TEXT && preText.endsWith( " if( elem.getType() == SyntaxElement.TYPE_ERROR ) { elem = elem.getPrevious(); if( elem == null ) return null; } if( elem.getType() == SyntaxElement.TYPE_TAG ) { SyntaxElement.Tag tagElem = (SyntaxElement.Tag)elem; String tagName = tagElem.getName().toUpperCase(); DTD.Element tag = dtd.getElement( tagName ); if( tag == null ) return null; // unknown tag TokenItem argItem = prev; while( argItem != null && argItem.getTokenID() != HTMLTokenContext.ARGUMENT ) argItem = argItem.getPrevious(); if( argItem == null ) return null; // no ArgItem String argName = argItem.getImage().toLowerCase(); DTD.Attribute arg = tag.getAttribute( argName ); if( arg == null || arg.getType() != DTD.Attribute.TYPE_SET ) return null; if( id != HTMLTokenContext.VALUE ) { len = 0; l = translateValues( offset-len, len, arg.getValueList( "" ) ); } else { len = offset - itemOffset; l = translateValues( offset-len, len, arg.getValueList( preText ) ); } } } //System.err.println("l = " + l ); if( l == null ) return null; else return new CompletionQuery.DefaultResult( component, "Results for DOCTYPE " + dtd.getIdentifier(), l, offset, len ); // NOI18N } catch (BadLocationException e) { e.printStackTrace(); } return null; } List translateCharRefs( int offset, int length, List refs ) { List result = new ArrayList( refs.size() ); for( Iterator i = refs.iterator(); i.hasNext(); ) { result.add( new CharRefItem( ((DTD.CharRef)i.next()).getName(), offset, length )); } return result; } List translateTags( int offset, int length, List tags ) { List result = new ArrayList( tags.size() ); for( Iterator i = tags.iterator(); i.hasNext(); ) { result.add( new TagItem( ((DTD.Element)i.next()).getName(), offset, length )); } return result; } List translateAttribs( int offset, int length, List attribs ) { List result = new ArrayList( attribs.size() ); for( Iterator i = attribs.iterator(); i.hasNext(); ) { DTD.Attribute attrib = (DTD.Attribute)i.next(); String name = attrib.getName(); switch( attrib.getType() ) { case DTD.Attribute.TYPE_BOOLEAN: result.add( new BooleanAttribItem( name, offset, length, attrib.isRequired() ) ); break; case DTD.Attribute.TYPE_SET: result.add( new SetAttribItem( name, offset, length, attrib.isRequired() ) ); break; case DTD.Attribute.TYPE_BASE: result.add( new PlainAttribItem( name, offset, length, attrib.isRequired() ) ); break; } } return result; } List translateValues( int offset, int length, List values ) { if( values == null ) return new ArrayList( 0 ); List result = new ArrayList( values.size() ); for( Iterator i = values.iterator(); i.hasNext(); ) { result.add( new ValueItem( ((DTD.Value)i.next()).getName(), offset, length )); } return result; } // Implementation of ResultItems for completion /** The simple result item operating over an instance of the string, * it is lightweight in the mean it doesn't allocate any new instances * of anything and every data creates lazily on request to avoid * creation of lot of string instances per completion result. */ private static abstract class HTMLResultItem implements CompletionQuery.ResultItem { /** The Component used as a rubberStamp for painting items */ static javax.swing.JLabel rubberStamp = new javax.swing.JLabel(); static { rubberStamp.setOpaque( true ); } /** The String on which is this ResultItem defined */ String baseText; /** the remove and insert point for this item */ int offset; /** The length of the text to be removed */ int length; public HTMLResultItem( String baseText, int offset, int length ) { this.baseText = lowerCase ? baseText.toLowerCase() : baseText.toUpperCase(); this.offset = offset; this.length = length; } boolean replaceText( JTextComponent component, String text ) { BaseDocument doc = (BaseDocument)component.getDocument(); doc.atomicLock(); try { doc.remove( offset, length ); doc.insertString( offset, text, null); } catch( BadLocationException exc ) { return false; //not sucessfull } finally { doc.atomicUnlock(); } return true; } public boolean substituteCommonText( JTextComponent c, int a, int b, int subLen ) { return replaceText( c, getItemText().substring( 0, subLen ) ); } public boolean substituteText( JTextComponent c, int a, int b, boolean shift ) { return replaceText( c, getItemText() ); } /** @return Properly colored JLabel with text gotten from |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.