|
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-2003 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.java.codegen; import java.beans.*; import java.lang.reflect.Method; import java.io.Writer; import java.io.PrintWriter; import java.io.StringWriter; import java.util.*; import javax.swing.text.*; import org.openide.src.*; import org.openide.src.Element; import org.openide.text.*; import org.netbeans.modules.java.bridge.Binding; /** * * @author svata * @version */ abstract class ElementBinding implements TextBinding, ElementProperties { private Element el; /** Bounds of the element as whole. */ protected PositionBounds wholeBounds; /** Bounds for the documentation. Null, if the documentation * does not exist. */ protected PositionBounds docBounds; /** Bounds of the element's header (exact meaning depends on the element type) */ protected PositionBounds headerBounds; /** Bounds of the body of the element. */ protected PositionBounds bodyBounds; /** SourceText text. */ protected SourceText source; protected ContainerImpl containerRef; /** * Cached Methods used for setting property values. */ static Map setterCache; private static final boolean DEBUG = false; /** * System-dependent line separator, or null if the system sep is not * defined or is single LF char. */ private static final String lineSeparator; /** * Length of the line separator string. */ private static final int lineSeparatorLength; /** * Linefeed string, used internally to separate lines. */ private static final String LINEFEED = "\n"; // NOI18N static { String sep = System.getProperty("line.separator"); // NOI18N if (sep == null || LINEFEED.equals(sep)) { lineSeparator = null; lineSeparatorLength = -1; } else { lineSeparator = sep; lineSeparatorLength = sep.length(); } } /** Creates new ElementBinding */ public ElementBinding(Element el, SourceText s) { this.el = el; this.source = s; s.registerBinding(el, this); } public TextBinding.Container getContainer(String type) { return containerRef; } public void linkAfter(TextBinding b) { } public Element getElement() { return this.el; } /** * Returns range occupied by this element. */ public PositionBounds getElementRange(boolean defRange) { if (!defRange || headerBounds == null) { return wholeBounds; } else { return new PositionBounds(headerBounds.getBegin(), wholeBounds.getEnd()); } } /** * Clones the target element. */ protected abstract Element cloneElement(); /** Actually creates the element in the SourceText text. This is called by the container * binding's insertAfter implementation to actually insert the element. The default * implementation will print the element through the ElementPrinter and compute * positions of the element's parts. */ public void create(PositionBounds bounds) throws SourceException { wholeBounds = bounds; if (DEBUG) { System.err.println("Pre-create dump:\n----------------------------------------------------"); // NOI18N System.err.println("element: " + el); // NOI18N System.err.println("creating at " + bounds); // NOI18N source.dumpDocument(); } regenerateWhole(this.el, true); if (DEBUG) { System.err.println("after create wholeBounds = " + wholeBounds); // NOI18N } } /** * Prepares the binding for insertion of its sibling. This implementation does nothing, * but bindings that are bound together in a textual order may perform some tasks * in this methods. */ public PositionRef prepareInsert(ElementBinding tbi, boolean after) throws SourceException { return after ? wholeBounds.getEnd() : wholeBounds.getBegin(); } /** Initializes the new binding with a position right after this element's own pos, * optionally creating an empty line for the element, if needed. * The implementation assumes that somebody else has already locked the document for * writing, so that all offset operations are thread-safe. */ public void insertAfter(ElementBinding b) throws SourceException { b.createAt(wholeBounds.getEnd()); } /** * Generates the associated element representation and associates the binding * with a particular container. The representation is inserted between other * bindings, "previous" and "next". Container bounds are passed to aid in case * of insertion at the container's boundary. emptyBefore and emptyAfter advises * whether the container wants the representation to separate itself from the * previous, or the next element. */ public void create(ContainerImpl container, ElementBinding previous, ElementBinding next, PositionBounds containerBounds, boolean emptyBefore, boolean emptyAfter) throws SourceException { PositionRef beginPos, top; if (previous == null) { beginPos = containerBounds.getBegin(); } else { beginPos = previous.prepareInsert(this, true); } if (next == null) { top = containerBounds.getEnd(); } else { top = next.prepareInsert(this, false); } PositionBounds gap = new PositionBounds( beginPos, top); PositionRef pos = source.findFreePosition(gap); if (pos == null) throw new SourceException("No room for element"); // NOI18N createAt(pos, emptyBefore, emptyAfter); containerRef = container; } private void createAt(PositionRef r, boolean sepBefore, boolean sepAfter) throws SourceException { PositionBounds newBounds = CodeGenerator.createNewLineBounds(r, CodeGenerator.BOUNDS_ALL, sepBefore, sepAfter); create(newBounds); } private void createAt(PositionRef r) throws SourceException { PositionBounds newBounds = CodeGenerator.createNewLineBounds(r, CodeGenerator.BOUNDS_ALL); create(newBounds); } public void applyPropertyChange(Object bean, String property, Object value) throws SourceException { java.lang.reflect.Method setter; if (setterCache == null) { setterCache = new HashMap(29); setter = null; } else { setter = (java.lang.reflect.Method)setterCache.get(property); } if (setter == null) { try { BeanInfo binfo = Introspector.getBeanInfo(bean.getClass()); PropertyDescriptor[] desc = binfo.getPropertyDescriptors(); for (int i = 0; i < desc.length; i++) { if (desc[i].getName().equals(property)) { setter = desc[i].getWriteMethod(); } } } catch (IntrospectionException ex) { } if (setter == null) { throw new SourceException("Cannot find setter for " + property); // NOI18N } setterCache.put(property, setter); } try { setter.invoke(bean, new Object[] { value }); return; } catch (IllegalAccessException ex) { } catch (java.lang.reflect.InvocationTargetException ex) { } } public void changeElementProperty(final PropertyChangeEvent evt) throws SourceException { if (!source.isGeneratorEnabled()) return; source.runAtomic(el, new ExceptionRunnable() { public void run() throws Exception { doChangeProperty(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue()); } }); } static final int CLASS_IGNORE = 0; static final int CLASS_HEADER = 1; static final int CLASS_BODY = 2; static final int CLASS_JAVADOC = 3; protected void doChangeProperty(String property, Object old, Object now) throws Exception { int propClass = classifyProperty(property); if (propClass == CLASS_IGNORE) return; Element clonedBean = cloneElement(); applyPropertyChange(clonedBean, property, now); switch (propClass) { case CLASS_HEADER: regenerateHeader(clonedBean); break; case CLASS_BODY: regenerateBody(clonedBean); break; case CLASS_JAVADOC: regenerateJavaDoc(clonedBean, getJavaDoc()); break; } } protected JavaDoc getJavaDoc() { throw new UnsupportedOperationException("Generic javadoc not supported!"); // NOI18N } protected int classifyProperty(String propName) { return CLASS_IGNORE; } public ElementBinding findBindingAt(int off) { if (wholeBounds.getBegin().getOffset() <= off && wholeBounds.getEnd().getOffset() > off) return this; else return null; } /** * Removes the element from the SourceText. * @return UndoableEdit operation that will result in re-inserting of the element in * the storage */ protected void remove(boolean collapseBefore, boolean collapseAfter) throws SourceException, IllegalStateException { // PENDING: record previous bounds for the case of a rollback. try { CodeGenerator.clearBounds(wholeBounds, collapseBefore); } catch (Exception ex) { SourceText.rethrowException(el,ex); } } protected void moveTo(ElementBinding before, ElementBinding after) throws SourceException { // PENDING: Undo the bounds change! PositionBounds oldWholeBounds = wholeBounds; containerRef.insertChild(this, before, after); try { CodeGenerator.clearBounds(oldWholeBounds, false); } catch (BadLocationException ex) { SourceText.rethrowException(getElement(),ex); } } /* ================ CODE GENERATION SUPPORT ======================== */ /** * The method retrieves a document for the element. If the document is not already * opened, the method opens the document and returns the instance. The method may * throw SourceException.IO, that contains the reason of failure reported by * FileSystem/EditSupport layer. */ protected StyledDocument findDocument() throws SourceException { return source.getDocument(); } /** * Replaces text for element's header with new contents taken from the parameter. * The implementation changes the document inside atomic lock. */ protected void regenerateHeader(Element el) throws SourceException { Document doc = findDocument(); source.runAtomic(this.el, new PartialGenerator(doc, el, headerBounds, ElementPrinter.HEADER_BEGIN, ElementPrinter.HEADER_END)); } protected void regenerateWhole(Element el, boolean updatePositions) throws SourceException { Document doc = findDocument(); source.runAtomic(this.el, new PartialGenerator(doc, el, updatePositions)); } protected void regenerateBody(Element el) throws SourceException { Document doc = findDocument(); source.runAtomic(this.el, new PartialGenerator(doc, el, bodyBounds, ElementPrinter.BODY_BEGIN, ElementPrinter.BODY_END)); } /** Regenerates javadoc for the element. The method assumes, that there IS some javadoc * on the element */ protected void regenerateJavaDoc(Element el, JavaDoc javadoc) throws SourceException { Document doc = findDocument(); if (docBounds != null) source.runAtomic(this.el, new PartialGenerator(doc, el, docBounds, ElementPrinter.JAVADOC_BEGIN, ElementPrinter.JAVADOC_END)); else source.runAtomic(this.el, new JavaDocGenerator(doc, el, javadoc)); } public boolean canInsertAfter() { return true; } protected class RemovingRunnable implements ExceptionRunnable { public void run() throws Exception { CodeGenerator.clearBounds(wholeBounds, true); } } public void changeJavaDoc(JavaDoc content) throws SourceException { if (!source.isGeneratorEnabled()) return; regenerateJavaDoc(this.el, content); } public void updateBounds(int kind, PositionBounds bounds) { switch (kind) { case BOUNDS_ALL: wholeBounds = bounds; break; case BOUNDS_JAVADOC: docBounds = bounds; break; case BOUNDS_BODY: bodyBounds = bounds; break; case BOUNDS_HEADER: headerBounds = bounds; } } protected class JavaDocGenerator extends PartialGenerator { JavaDoc content; JavaDocGenerator(Document doc, Element el, JavaDoc javadoc) { super(doc, el, null, ElementPrinter.JAVADOC_BEGIN, ElementPrinter.JAVADOC_END); content = javadoc; } public void run() throws Exception { String raw = content.getRawText(); if (content.isEmpty()) { // clear the javadoc from the source. if (docBounds != null) { CodeGenerator.clearBounds(docBounds, false); docBounds = null; } } else { posBounds = CodeGenerator.createNewLineBounds( wholeBounds.getBegin(), CodeGenerator.BOUNDS_JAVADOC); super.run(); docBounds = posBounds; wholeBounds = new PositionBounds(posBounds.getBegin(), wholeBounds.getEnd()); } } } /** * Helper class used for generating headers in {@link #regenerateHeader method} */ protected class PartialGenerator implements ExceptionRunnable { PositionBounds posBounds; int begin; int end; Document doc; Element el; boolean update; PartialGenerator(Document doc, Element el, boolean updateBounds) { update = updateBounds; this.el = el; this.doc = doc; begin = ElementPrinter.ELEMENT_BEGIN; end = ElementPrinter.ELEMENT_END; posBounds = wholeBounds; } PartialGenerator(Document doc, Element el, PositionBounds pos, int begin, int end) { this.posBounds = pos; this.begin = begin; this.end = end; this.doc = doc; this.el = el; } public void run() throws Exception { PositionRef headerBegin = posBounds.getBegin(); StyledDocument doc = findDocument(); StringWriter wr = new StringWriter(); Writer indentWriter = CodeGenerator.findIndentWriter(doc, headerBegin.getOffset(), wr); ElementPrinterImpl printer = update ? new WholeElementPrinter(indentWriter, wr) : new ElementPrinterImpl(indentWriter, el, begin, end); try { el.print(printer); } catch (ElementPrinterInterruptException e) { } try { indentWriter.close(); } catch (java.io.IOException ex) { throw new InternalError(ex.getMessage()); } CodeGenerator.fillTextBounds(posBounds, wr.toString()); if (update) { ((WholeElementPrinter)printer).finish(); } } } PositionRef getEndPosition() { StyledDocument doc = source.getEditorSupport().getDocument(); return source.createPos(doc.getLength(), Position.Bias.Backward); } PositionBounds findContainerBounds(TextBinding.Container cont) { throw new UnsupportedOperationException(this.toString()); } class WholeElementPrinter extends ElementPrinterImpl { StringWriter stringWriter; CloneableEditorSupport editor; int[] positions; WholeElementPrinter(Writer writer, StringWriter stringWriter /* Element element, ElementPositions poss, CloneableEditorSupport editor */) { super(writer); this.stringWriter = stringWriter; /* this.element = element; this.poss = poss; this.editor = editor; */ this.editor = source.getEditorSupport(); this.positions = new int[8]; java.util.Arrays.fill(positions, -1); } public void markNotify(Element el, int what) { if (lastText!=null && lastText.endsWith(" ")) { // NOI18N String writerText=stringWriter.getBuffer().toString(); int len=lastText.length(); if (len>0 && !writerText.endsWith(" ")) { // NOI18N char ch=0; // initialized only to keep compiler happy int i; for (i=len-1;i>=0;i--) { ch=lastText.charAt(i); if (ch!=' ') break; } if (ch!='\n') { i++; stringWriter.write(lastText,i,len-i); } } } positions[what] = stringWriter.getBuffer().length(); if (what==ELEMENT_END && positions[BODY_END]!=-1 && (el instanceof ConstructorElement || el instanceof InitializerElement)) positions[BODY_END]=positions[ELEMENT_END]-1; } void finish() { int offset = wholeBounds.getBegin().getOffset(); if (positions[ELEMENT_BEGIN] != -1 && positions[ELEMENT_END] != -1) { wholeBounds = createStableBounds(positions[ELEMENT_BEGIN] + offset, positions[ELEMENT_END] + offset); } //System.err.println("Element " + System.identityHashCode(getElement())); // NOI18N //System.err.println("wholeBounds = " + wholeBounds); // NOI18N if (positions[JAVADOC_BEGIN] != -1 && positions[JAVADOC_END] != -1) { docBounds = createStableBounds(positions[JAVADOC_BEGIN] + offset, positions[JAVADOC_END] + offset); } //System.err.println("docBounds = " + docBounds); // NOI18N if (positions[HEADER_BEGIN] != -1 && positions[HEADER_END] != -1) { headerBounds = createStableBounds(positions[HEADER_BEGIN] + offset, positions[HEADER_END] + offset); } //System.err.println("headerBounds = " + headerBounds); // NOI18N if (positions[BODY_BEGIN] != -1 && positions[BODY_END] != -1) { bodyBounds = createBounds(positions[BODY_BEGIN] + offset, positions[BODY_END] + offset); } //System.err.println("bodyBounds = " + bodyBounds); // NOI18N } private PositionBounds createStableBounds(int begin, int end) { if ((begin == -1) || (end == -1)) return null; PositionRef posBegin = editor.createPositionRef(begin, Position.Bias.Backward); PositionRef posEnd = editor.createPositionRef(end, Position.Bias.Forward); return new PositionBounds(posBegin, posEnd); } private PositionBounds createBounds(int begin, int end) { if ((begin == -1) || (end == -1)) return null; PositionRef posBegin = editor.createPositionRef(begin, Position.Bias.Backward); PositionRef posEnd = editor.createPositionRef(end, Position.Bias.Forward); return new PositionBounds(posBegin, posEnd); } } public String toString() { return "Binding for " + getElement(); // NOI18N } static String convertNewlines(String input) { if (lineSeparator == null) return input; int firstIndex; //System.err.println("original length = " + input.length()); // NOI18N /* for (int i = 0; i < input.length(); i++) { System.err.print(((int)input.charAt(i)) + ", "); } */ //System.err.println(""); firstIndex = input.indexOf(lineSeparator); if (firstIndex == -1) return input; //System.err.println("Converting newlines..."); // NOI18N // replace from the beginning to the firstIndex StringBuffer result = new StringBuffer(); char[] contents = input.toCharArray(); if (firstIndex > 0) result.append(contents, 0, firstIndex); result.append(LINEFEED); firstIndex += lineSeparatorLength; int lastPos = firstIndex; while (firstIndex < contents.length) { firstIndex = input.indexOf(lineSeparator, firstIndex); if (firstIndex == -1) { // put there the rest of the string. result.append(contents, lastPos, contents.length - lastPos); return result.toString(); } // put the portion not containing the separator. result.append(contents, lastPos, firstIndex - lastPos); result.append(LINEFEED); firstIndex += lineSeparatorLength; lastPos = firstIndex; } // execution comes here only if the line.sep is the last thing // in the string. result.append(contents, lastPos, firstIndex - lastPos); //System.err.println("result length = " + result.length()); // NOI18N return result.toString(); } // ================= methods ============================== static class ElementPrinterImpl implements ElementPrinter { PrintWriter writer; String lastText=null; Element printedElement; int beginMark; int endMark; int status; ElementPrinterImpl(Writer writer) { this(writer, null, 0, 0); status = 1; } ElementPrinterImpl(Writer writer, Element printedElement, int beginMark, int endMark) { this.writer = new PrintWriter(writer); this.printedElement = printedElement; this.beginMark = beginMark; this.endMark = endMark; status = 0; } public boolean isBegin(Element element, int what) { return (printedElement == null) || ((element == printedElement) && (what == beginMark)); } public boolean isEnd(Element element, int what) { return (printedElement == element) && (what == endMark); } public void markNotify(Element element, int what) { } public String getString() { return writer.toString(); } /** Prints the given text. * @param text The text to write */ public void print(String text) throws ElementPrinterInterruptException { switch (status) { case 0: lastText=null; return; case 1: text = convertNewlines(text); lastText=text; writer.print(text); break; case 2: throw new ElementPrinterInterruptException(); } } /** Prints the line. New-line character '\n' should be added. * @param text The line to write */ public void println(String text) throws ElementPrinterInterruptException { print(text); print("\n"); // NOI18N } /** Add the mark to the list, if the printer is currently caching * (status == 1) or this mark is the begin. * @param element The element to mark * @param what The kind of event */ private void mark(Element element, int what) throws ElementPrinterInterruptException { switch (status) { case 0: if (isBegin(element, what)) { markNotify(element, what); status = 1; } break; case 1: writer.flush(); markNotify(element, what); if (isEnd(element, what)) { status = 2; writer.close(); throw new ElementPrinterInterruptException(); } break; case 2: throw new ElementPrinterInterruptException(); } } public void markClass(ClassElement element, int what) throws ElementPrinterInterruptException { mark(element, what); } public void markInitializer(InitializerElement element, int what) throws ElementPrinterInterruptException { mark(element, what); } public void markField(FieldElement element, int what) throws ElementPrinterInterruptException { mark(element, what); } public void markConstructor(ConstructorElement element, int what) throws ElementPrinterInterruptException { mark(element, what); } public void markMethod(MethodElement element, int what) throws ElementPrinterInterruptException { mark(element, what); } } } |
... 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.