alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

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.modules.java.codegen;

import java.io.*;
import javax.swing.text.*;

import org.openide.src.*;
import org.openide.text.*;

import org.netbeans.modules.java.bridge.Binding;

/**
 *
 * @author  Svatopluk Dedic 
 * @version 0.1
 */
class FieldB extends Member implements Binding.Field {
    private static final boolean DEBUG = false;
    
    /** Links to the previous and next fields in the field declarator.
     * If both are null, the field is declared alone and single should be set to
     * true.
     */
    FieldB   previous, next;
    
    /** Bounds that contain the type declaration.
     */
    public  PositionBounds typeBounds;
    
    boolean single;
    
    public FieldB(FieldElement el, SourceText s) {
        super(el, s);
        single = true;
    }
    
    
    /**
     * Links another Field after this one; those fields will act as a field group.
     */
    public void linkAfter(TextBinding t) {
        FieldB f = (FieldB)t;
        if (next == f)
            return;
        
        if (next != null) {
            next.previous = f;
            next.single = f == null && next.next == null;
            next.adjustBounds();
        }
        if (f != null) {
            f.previous = this;
            f.next = next;
            f.single = false;
            single = false;
            if (typeBounds != null)
                f.typeBounds = typeBounds;
        } else {
            single = previous == null;
            adjustBounds();
        }
        next = f;
    }
    
    
    public void linkBefore(TextBinding t) {
        FieldB f = (FieldB)t;
        if (previous == f)
            return;
        
        if (previous != null) {
            previous.next = f;
            previous.single = f == null && previous.previous == null;
            previous.adjustBounds();
        }
        if (f != null) {
            f.previous = previous;
            f.next = this;
            f.single = false;
            single = false;
        } else {
            single = next == null;
            adjustBounds();
        }
        previous = f;
    }
    
    private boolean isSingle() {
        return single;
    }
    
    private void adjustBounds() {
        if (!isSingle() || typeBounds == null)
            return;
        if (DEBUG)
            System.err.println("Adjusting bounds for single field..."); // NOI18N
        headerBounds = new PositionBounds(typeBounds.getBegin(), headerBounds.getEnd());
        if (docBounds == null) 
            wholeBounds = new PositionBounds(typeBounds.getBegin(), wholeBounds.getEnd());
        else
            wholeBounds = new PositionBounds(docBounds.getBegin(), wholeBounds.getEnd());
        
        typeBounds = null;
    }

    private FieldElement cloneField() {
        return (FieldElement)cloneElement();
    }
    
    protected int classifyProperty(String name) {
        if (name == PROP_INIT_VALUE)
            return CLASS_BODY;
        else
            return CLASS_HEADER;
    }
    
    public void updateBounds(int kind, PositionBounds b) {
        if (kind == BOUNDS_FIELD_TYPE) {
            if (typeBounds == null || b != null)
                this.typeBounds = b;
        } else {
            super.updateBounds(kind, b);
        }
    }
    
    protected void regenerateBody(org.openide.src.Element el) throws SourceException {
        regenerateInitializer((FieldElement) el);
    }
    
    protected void doChangeProperty(String property, Object old, final Object now)
    throws Exception {
        if (isSingle() || property == PROP_INIT_VALUE) {
            super.doChangeProperty(property, old, now);
            return;
        }
        
        if (property == PROP_NAME) {
            source.runAtomic(getElement(), new ExceptionRunnable() {
                public void run() throws Exception {
                    CodeGenerator.fillTextBounds(headerBounds, 
                        ((Identifier)now).getSourceName());
                }
            });
        } else {
            FieldElement f = cloneField();
            applyPropertyChange(f, property, now);
            new SingleMaker(f).run();
        }
    }
    
    /** Changes the declared type of the field.
     */
    public void changeType(Type newType) throws SourceException {
        if (!source.isGeneratorEnabled())
            return;
        
        if (isSingle()) {
            FieldElement f = cloneField();
            f.setType(newType);
            regenerateHeader(f);
            return;
        }
        FieldElement f = cloneField();
        f.setType(newType);
        source.runAtomic(getElement(), new SingleMaker(f));
    }
    
    private void regenerateJavaDoc() throws SourceException {
        super.regenerateJavaDoc(getElement(), ((FieldElement)getElement()).getJavaDoc());
    }

    /**
     * Prepares the field for insertion of a sibling. Since the operation is
     * symmetric, and we only care for insertion between two siblings, move the burden
     * to the field that precede the new sibling.
     */
    public PositionRef prepareInsert(ElementBinding tbi, boolean after) 
    throws SourceException {
        if (after && next != null) {
            breakFieldGroup();
            if (DEBUG)
                source.dumpDocument();
        } else if (!after && previous != null) {
            previous.breakFieldGroup();
            if (DEBUG)
                source.dumpDocument();
        }
        if (DEBUG) {
            System.err.println("prepareInsert: after = " + after + "wholeBounds = " + wholeBounds); // NOI18N
        }
        return super.prepareInsert(tbi, after);
    }
    
    /** Breaks the field group after _this_ field's declaration.
     */
    void breakFieldGroup() throws SourceException {
        try {
            final String headerText = typeBounds.getText();
            final FieldB prev = this.previous;
            final FieldB next = this.next;
            final StyledDocument doc = findDocument();

            if (DEBUG) {
                System.err.println("breakFieldGroup invoked on " + getField()); // NOI18N
                System.err.println("[breakFieldGroup] headerText = " + headerText); // NOI18N
                System.err.println("[breakFieldGroup] myBounds = " + wholeBounds); // NOI18N
                dumpBoundsForChain();
            }
            if (next == null) {
                throw new IllegalStateException("breakFieldGroup invoked on field " // NOI18N
                    + ((FieldElement)getElement()).getName() + " that is at the end of the group"); // NOI18N
            }
            PositionBounds insertion;


            // this is because we don't want to extend (body)Bounds:
            // 1. turn the last character to a semicolon.
            // 2. insert a newline (formatted through the indent engine)
            // 3. insert type specification (again formatted)
            PositionBounds replacement = new PositionBounds(
                source.createPos(
                    wholeBounds.getEnd().getOffset() - 1,
                    Position.Bias.Backward
                ),
                source.createPos(
                    wholeBounds.getEnd().getOffset(),
                    Position.Bias.Forward
                )
            );
            CodeGenerator.fillTextBounds(replacement, 
                CodeGenerator.formatText(doc, replacement.getBegin(), ";")); // NOI18N

            String separator = CodeGenerator.formatText(doc, replacement.getEnd(),
                "\n" + headerText) + " "; // NOI18N
            int hdrOffset = separator.indexOf(headerText);

            PositionBounds insertion2 = new PositionBounds(
                source.createPos(
                    replacement.getEnd().getOffset(),
                    Position.Bias.Forward
                ),
                source.createPos(
                    next.headerBounds.getBegin().getOffset(),
                    Position.Bias.Backward
                )
            );
            insertion2.setText(separator);
            
            PositionBounds tBounds = new PositionBounds(
                source.createPos(insertion2.getBegin().getOffset() + hdrOffset, Position.Bias.Backward),
                source.createPos(insertion2.getEnd().getOffset() - 1, Position.Bias.Forward)
            );

            /*
            insertion = wholeBounds.insertAfter(
                    CodeGenerator.formatText(doc, replacement.getEnd(), "\n") // NOI18N
                );

            PositionBounds insertion2 = new PositionBounds(
                source.createPos(
                    insertion.getEnd().getOffset(),
                    Position.Bias.Forward
                ),
                source.createPos(
                    next.headerBounds.getBegin().getOffset(),
                    Position.Bias.Backward
                )
            );
             */

            if (DEBUG) {
                System.err.println("[breakFieldGroup] Insertion for type is " + insertion2 +  // NOI18N
                    " hdr offset = " + hdrOffset); // NOI18N
            }
            /*
            // insert a copy of type specification for the rest of the fields
            insertion2.setText(headerText + " ");

            // type bounds have to stick with the type.
            PositionBounds tBounds = new PositionBounds(
                insertion.getBegin(),
                source.createPos(
                    insertion2.getEnd().getOffset(),
                    Position.Bias.Forward
                )
            );
             */

            if (DEBUG) {
                System.err.println("[breakFieldGroup] next's type bounds = " + tBounds); // NOI18N
            }

            // adjust the bounds for the immediate next field and clear it's 
            // JavaDoc bounds since it hasn't any JavaDoc yet.
            next.typeBounds = tBounds;
            next.wholeBounds = new PositionBounds(
                tBounds.getBegin(),
                next.wholeBounds.getEnd()
            );
            // this should set next.previous -> null, this->next to null, next->single to true,
            // this->single to 
            next.linkBefore(null);
            // clear the next fields' docBounds.
            next.docBounds = null;

            // set typeBounds for all fields starting with `next' and duplicate
            // javadoc/bounds for the rest of next's group.
            for (FieldB fimpl = next.next; fimpl != null; fimpl = fimpl.next) {
                fimpl.typeBounds = tBounds;
                fimpl.docBounds = next.docBounds;
            }

            if (DEBUG) {
                if (prev != null) {
                    System.err.println("Dumping prev chain:"); // NOI18N
                    prev.dumpBoundsForChain();
                }
                System.err.println("Dumping next chain:"); // NOI18N
                next.dumpBoundsForChain();
            }
            
            if (docBounds != null) {
                // regenerate -- this WILL create a DocBounds for the javadoc,
                // it is based on an assumption, that bounds are correct -->
                // javadoc will be OK.
                next.regenerateJavaDoc();
            }
            adjustBounds();
            if (DEBUG) {
                System.err.println("breakFieldGroup report:"); // NOI18N
                System.err.println("-- first part tail details:"); // NOI18N
                System.err.println("whole = " + wholeBounds + " header = " + headerBounds // NOI18N
                    + " body = " + bodyBounds + " type = " + typeBounds + " single = " + isSingle()); // NOI18N
                System.err.println("-- second part head details:"); // NOI18N
                System.err.println("whole = " + next.wholeBounds + " header = " + next.headerBounds // NOI18N
                    + " body = " + next.bodyBounds + " type = " + next.typeBounds + " single = " + next.isSingle()); // NOI18N
                System.err.println("link deatils:"); // NOI18N
                System.err.println("tail = " + this + " head = " + next + " tail's next = " + this.next +  // NOI18N
                    " head's prev = " + next.previous); // NOI18N
                source.dumpDocument();
            }
        } catch (Exception ex) {
            SourceText.rethrowException(getElement(), ex);
        }
    }
    
    /** Changes contents of the initializer of the field.
     */
    public void changeInitializer(String newInitializer) throws SourceException {
        if (!source.isGeneratorEnabled())
            return;
        
        FieldElement f = cloneField();
        f.setInitValue(newInitializer);
        regenerateInitializer(f);
    }
    
    protected void regenerateWhole(org.openide.src.Element model, boolean updatePositions) 
    throws SourceException {
        super.regenerateWhole(model, updatePositions);
        int headerEndOffset = headerBounds.getEnd().getOffset();
        
        if (updatePositions && (((FieldElement)model).getInitValue().length() > 0)) {
            // adjust start of a body to point right after name, if the model has some
            // initial value:
            bodyBounds = new PositionBounds(
                source.createPos(headerEndOffset, Position.Bias.Backward),
                bodyBounds.getEnd());
        } else {
            // defense against null or ill-formed body bounds:
            bodyBounds = null;
        }
    }
    
    /**
     * Requests change of member's modifiers.
     */
    public void changeModifiers(int newMods) throws SourceException {
        if (!source.isGeneratorEnabled())
            return;
        
        if (isSingle()) {
            super.changeModifiers(newMods);
            return;
        }
        
        FieldElement f = cloneField();
        f.setModifiers(newMods);
        source.runAtomic(getElement(), new SingleMaker(f));
    }
    
    /**
     * Requests a change of member's name.
     */
    public void changeName(final Identifier name) throws SourceException {
        if (!source.isGeneratorEnabled())
            return;
        
        if (isSingle()) {
            super.changeName(name);
            return;
        }
        source.runAtomic(getElement(), new ExceptionRunnable() {
            public void run() throws Exception {
                CodeGenerator.fillTextBounds(headerBounds, name.getSourceName());
            }
        });
    }
    
    /**
     * Removes the element from the SourceText.
     * @return UndoableEdit operation that will result in re-inserting of the element in
     * the storage
     */
    public void remove(boolean collapseBefore, boolean collapseAfter) throws SourceException, IllegalStateException {
        if (isSingle()) {
            super.remove(collapseBefore, collapseAfter);
            return;
        }
        removeFromGroup();
    }
    
    /**
     * Updates the storage binding object from an external SourceText.
     */
    public void updateFrom(Binding other) {
    }
    
    public void changeJavaDoc(JavaDoc content) throws SourceException {        
        if (!source.isGeneratorEnabled())
            return;
        if (!isSingle()) {
            FieldElement f=(FieldElement)getElement();
            source.runAtomic(f, new SingleMaker(f));
        }
        super.changeJavaDoc(content);     
    }

    private void regenerateInitializer(final FieldElement fld) throws SourceException {
        final StyledDocument doc;

        doc = source.getDocument();

        ExceptionRunnable run = new ExceptionRunnable() {
            public void run() throws Exception {
                String txt;
                String init = fld.getInitValue();
		int start;
		
		if (bodyBounds == null) {
		    start = wholeBounds.getEnd().getOffset() - 1;
		} else {
		    start = bodyBounds.getBegin().getOffset();
		}
                
                if (init != null &&
                !init.equals("")) {
                    StringWriter writer = new StringWriter();
                    Writer iWriter = CodeGenerator.findIndentWriter(doc, start, writer);
                    iWriter.write(" = "); // NOI18N
                    ElementPrinter prn = new ElementPrinterImpl(iWriter, fld, ElementPrinter.BODY_BEGIN,
                    ElementPrinter.BODY_END);
                    try {
                        fld.print(prn);
                    } catch (ElementPrinterInterruptException e) {
                    }
                    txt = writer.toString();
                } else {
                    txt = "";
                }
                if (bodyBounds == null) {
                    bodyBounds = new PositionBounds(
                        headerBounds.getEnd(),
                        source.createPos(wholeBounds.getEnd().getOffset() - 1, Position.Bias.Backward));
                } else if (bodyBounds.getBegin().getOffset() == bodyBounds.getEnd().getOffset()) {
                    bodyBounds = new PositionBounds(
                        headerBounds.getEnd(), bodyBounds.getEnd()
                    );
                }
                bodyBounds.setText(txt);
            }
        };
        
        source.runAtomic(getElement(), run);
    }
    
    private void unlinkField() {
        FieldB p = previous;

        if (previous != null) {
            previous.next = next;
            previous.single = previous.previous == null && next == null;
            previous.adjustBounds();
        }
        if (next != null) {
            next.previous = p;
            next.single = next.next == null && p == null;
            next.adjustBounds();
        }
        previous = next = null;
        single = true;
        adjustBounds();
    }
    
    private FieldElement getField() {
        return (FieldElement)getElement();
    }

    void removeFromGroup() throws SourceException {
        PositionRef endRef;
        PositionBounds removal;
        
        if (DEBUG) {
            System.err.println("Field " + getField().getName().toString() + ": removing from SourceText file."); // NOI18N
            System.err.println("previous = " + (previous == null ? (Object)previous : (Object)previous.getField())); // NOI18N
            System.err.println("next = " + (next == null ? (Object)next : (Object)next.getField())); // NOI18N
            dumpBoundsForChain();
        }
        endRef = source.createPos(wholeBounds.getEnd().getOffset(), Position.Bias.Forward);
        if (previous != null) {
            if (DEBUG) {
                System.err.println("previous = " + previous + " f = " + previous.getField().getName().toString()); // NOI18N
                System.err.println("it's wholebounds = " + previous.wholeBounds); // NOI18N
            }
            removal = new PositionBounds(
            source.createPos(previous.wholeBounds.getEnd().getOffset() - 1, Position.Bias.Forward),
            source.createPos(wholeBounds.getEnd().getOffset() - 1, Position.Bias.Backward));
        } else {
            // We're the first field in the field element chain. We have to retain
            // the type declarator in place and remove only the name.
            removal = new PositionBounds(headerBounds.getBegin(), endRef);
        }
        // rebuild the field group links:
        if (previous != null) {
            // previous field ought to contain
            // the delimiter after the field being removed.
            // endRef points to the delimiter.
            // bounds ought to contain entire field including the delimiter.
            previous.wholeBounds = new PositionBounds(
                previous.wholeBounds.getBegin(),
                source.createPos(
                    endRef.getOffset(),
                    Position.Bias.Forward)
                );
            if (DEBUG) {
                System.err.println("[removeFromGroup] previous' bounds: " + // NOI18N
                    previous.wholeBounds);
            }
        }
        if (DEBUG) {
            System.err.println("removal bounds: " + removal); // NOI18N
        }
        try {
            CodeGenerator.clearBounds(removal, false);
            if (DEBUG) 
                source.dumpDocument();
        } catch (BadLocationException ex) {
            SourceText.rethrowException(getElement(),ex);
        }
        
        if (DEBUG) {
            System.err.println("breakFieldGroup report:"); // NOI18N
            System.err.println("-- first part tail details:"); // NOI18N
            if (previous != null) {
                System.err.println("whole = " + previous.wholeBounds + " header = " + previous.headerBounds // NOI18N
                    + " body = " + previous.bodyBounds + " type = " + previous.typeBounds + " single = " + previous.isSingle()); // NOI18N
                System.err.println("-- second part head details:"); // NOI18N
            }
            if (next != null) {
                System.err.println("whole = " + next.wholeBounds + " header = " + next.headerBounds // NOI18N
                    + " body = " + next.bodyBounds + " type = " + next.typeBounds + " single = " + next.isSingle()); // NOI18N
            }
            System.err.println("link deatils:"); // NOI18N
            System.err.println("tail = " + previous + " head = " + next); // NOI18N
            if (previous != null) {
                System.err.println(" tail's next = " + previous.next); // NOI18N
            }
            if (next != null) {
                System.err.println("head's prev = " + next.previous); // NOI18N
            }
            System.err.println("myPrevious = " + previous + "myNext = " + next); // NOI18N
        }
        FieldB p = previous, n = next;
        unlinkField();
        if (DEBUG) {
            System.err.println("link deatils after unlink:"); // NOI18N
            System.err.println("tail = " + p + " head = " + n); // NOI18N
            if (p != null) {
                System.err.println(" tail's next = " + p.next); // NOI18N
            }
            if (n != null) {
                System.err.println("head's prev = " + n.previous); // NOI18N
            }
            System.err.println("myPrevious = " + previous + "myNext = " + next); // NOI18N

            if (DEBUG) {
                if (p != null) {
                    System.err.println("Dumping previous chain:"); // NOI18N
                    p.dumpBoundsForChain();
                }
                if (n != null) {
                    System.err.println("Dumping next chain:"); // NOI18N
                    n.dumpBoundsForChain();
                }
            }
        }
        previous = next = null;
    }
    
    private void dumpBoundsForChain() {
        if (!DEBUG)
            return;
        FieldB begin = this;
        while (begin.previous != null)
            begin = begin.previous;
        
        while (begin != null) {
            begin.dumpFieldBounds();
            begin = begin.next;
        }
    }
    
    private void dumpFieldBounds() {
        if (!DEBUG)
            return;
        System.err.println("Dumping bounds for: " + getField() + "(" + this + ")"); // NOI18N
        System.err.println("header = " + headerBounds); // NOI18N
        System.err.println("body = " + bodyBounds); // NOI18N
        System.err.println("whole = " + wholeBounds); // NOI18N
        System.err.println("type = " + typeBounds); // NOI18N
        System.err.println("------------------------------------");
    }
    
    private class SingleMaker extends PartialGenerator {
        SingleMaker(FieldElement model) throws SourceException {
            super(FieldB.this.findDocument(), model, true);
        }
        
        private void makeSingle() throws Exception {
            ElementBinding p = previous;
            ElementBinding n = next;
            PositionBounds newBounds;
            
            if (p == null && n == null) {
                throw new IllegalStateException("Field does not refer to its siblings"); // NOI18N
            }
            if (p == null) {
                p = containerRef.findPrevious(FieldB.this);
            }
            if (n == null) {
                n = containerRef.findNext(FieldB.this);
            }
            // removes this field from its group.
            if (DEBUG) {
                System.err.println("[makeSingle] prevField = " + p +  // NOI18N
                    ", nextField = " + n); // NOI18N
            }
            removeFromGroup();
            if (DEBUG)
                source.dumpDocument();
            containerRef.insertChild(FieldB.this, p, n);
        }
        
        public void run() throws Exception {
            makeSingle();
            this.posBounds = wholeBounds;
            if (DEBUG) {
                System.err.println("Field made single:"); // NOI18N
                dumpBoundsForChain();
                source.dumpDocument();
            }
            super.run();
        }
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

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.