001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.editor.guards;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyVetoException;
046: import javax.swing.text.BadLocationException;
047: import javax.swing.text.Position;
048: import javax.swing.text.StyledDocument;
049: import org.netbeans.api.editor.guards.GuardedSection;
050: import org.openide.text.NbDocument;
051:
052: /** Represents one guarded section.
053: */
054: public abstract class GuardedSectionImpl {
055: /** Name of the section. */
056: String name;
057:
058: /** If the section is valid or if it was removed. */
059: boolean valid = false;
060:
061: final GuardedSectionsImpl guards;
062:
063: GuardedSection guard;
064:
065: /** Get the name of the section.
066: * @return the name
067: */
068: public String getName() {
069: return name;
070: }
071:
072: /** Creates new section.
073: * @param name Name of the new section.
074: */
075: GuardedSectionImpl(String name, GuardedSectionsImpl guards) {
076: this .name = name;
077: this .guards = guards;
078: }
079:
080: public final void attach(GuardedSection guard) {
081: this .guard = guard;
082: valid = true;
083: }
084:
085: /** Set the name of the section.
086: * @param name the new name
087: * @exception PropertyVetoException if the new name is already in use
088: */
089: public void setName(String name) throws PropertyVetoException {
090: if (!this .name.equals(name)) {
091: synchronized (this .guards.sections) {
092: if (valid) {
093: if (this .guards.sections.get(name) != null)
094: throw new PropertyVetoException("",
095: new PropertyChangeEvent(this , "name",
096: this .name, name)); // NOI18N
097: this .guards.sections.remove(this .name);
098: this .name = name;
099: this .guards.sections.put(name, this );
100: }
101: }
102: }
103:
104: }
105:
106: /** Deletes the text of the section and
107: * removes it from the table. The section will then be invalid
108: * and it will be impossible to use its methods.
109: */
110: public void deleteSection() {
111: synchronized (this .guards.sections) {
112: if (valid) {
113: try {
114: this .guards.sections.remove(name);
115: // get document should always return the document, when section
116: // is deleted, because it is still valid (and valid is only
117: // when document is loaded.
118: unmarkGuarded(this .guards.getDocument());
119: deleteText();
120: valid = false;
121: } catch (BadLocationException e) {
122: throw new IllegalStateException(e);
123: }
124: }
125: }
126: }
127:
128: /**
129: * Tests if the section is still valid - it is not removed from the
130: * source.
131: */
132: public boolean isValid() {
133: return valid;
134: }
135:
136: /**
137: * Removes the section from the Document, but retains the text contained
138: * within. The method should be used to unprotect a region of code
139: * instead of calling NbDocument.
140: * @return true if the operation succeeded.
141: */
142: public void removeSection() {
143: synchronized (this .guards.sections) {
144: if (valid) {
145: this .guards.sections.remove(name);
146: // get document should always return the document, when section
147: // is deleted, because it is still valid (and valid is only
148: // when document is loaded.
149: unmarkGuarded(this .guards.getDocument());
150: valid = false;
151: }
152: }
153: }
154:
155: /** Set the text contained in this section.
156: * Newlines are automatically added to all text segments handled,
157: * unless there was already one.
158: * All guarded blocks must consist of entire lines.
159: * This applies to the contents of specific guard types as well.
160: * @param bounds the bounds indicating where the text should be set
161: * @param text the new text
162: * @param minLen If true the text has to have length more than 2 chars.
163: * @return <code>true</code> if the operation was successful, otherwise <code>false</code>
164: */
165: protected boolean setText(PositionBounds bounds, String text,
166: boolean minLen) {
167: if (!valid)
168: return false;
169:
170: // modify the text - has to contain at least a space and the length
171: // has to be at least 1 character
172: if (minLen) {
173: if (text.length() == 0 || text.length() == 1
174: && text.equals("\n"))
175: text = " "; // NOI18N
176: }
177:
178: if (text.endsWith("\n")) // NOI18N
179: text = text.substring(0, text.length() - 1);
180:
181: try {
182: bounds.setText(text);
183: return true;
184: } catch (BadLocationException e) {
185: }
186: return false;
187: }
188:
189: /** Marks or unmarks the section as guarded.
190: * @param doc The styled document where this section placed in.
191: * @param bounds The rangeof text which should be marked or unmarked.
192: * @param mark true means mark, false unmark.
193: */
194: void markGuarded(StyledDocument doc, PositionBounds bounds,
195: boolean mark) {
196: int begin = bounds.getBegin().getOffset();
197: int end = bounds.getEnd().getOffset();
198: if (mark) {
199: NbDocument.markGuarded(doc, begin, end - begin + 1);
200: } else
201: NbDocument.unmarkGuarded(doc, begin, end - begin + 1);
202: }
203:
204: /** Marks the section as guarded.
205: * @param doc The styled document where this section placed in.
206: */
207: abstract void markGuarded(StyledDocument doc);
208:
209: /** Unmarks the section as guarded.
210: * @param doc The styled document where this section placed in.
211: */
212: abstract void unmarkGuarded(StyledDocument doc);
213:
214: /** Deletes the text in the section.
215: * @exception BadLocationException
216: */
217: final void deleteText() throws BadLocationException {
218: if (valid) {
219: final StyledDocument doc = guards.getDocument();
220: final BadLocationException[] blex = new BadLocationException[1];
221: NbDocument.runAtomic(doc, new Runnable() {
222: public void run() {
223: try {
224: int start = getStartPosition().getOffset();
225: if (start > 0
226: && "\n".equals(doc
227: .getText(start - 1, 1))) { // NOI18N
228: start--;
229: }
230: doc.remove(start, getEndPosition().getOffset()
231: - start + 1);
232: } catch (BadLocationException ex) {
233: blex[0] = ex;
234: }
235: }
236: });
237:
238: if (blex[0] != null) {
239: throw blex[0];
240: }
241: }
242: }
243:
244: /** Gets the begin of section. To this position is set the caret
245: * when section is open in the editor.
246: */
247: public abstract Position getCaretPosition();
248:
249: /** Gets the text contained in the section.
250: * @return The text contained in the section.
251: */
252: public abstract String getText();
253:
254: /** Assures that a position is not inside the guarded section. Complex guarded sections
255: * that contain portions of editable text can return true if the tested position is
256: * inside one of such portions provided that permitHoles is true.
257: * @param pos position in question
258: * @param permitHoles if false, guarded section is taken as a monolithic block
259: * without any holes in it regardless of its complexity.
260: */
261: public abstract boolean contains(Position pos, boolean permitHoles);
262:
263: /** Returns a position after the whole guarded block that is safe for insertions.
264: */
265: public abstract Position getEndPosition();
266:
267: /** Returns position before the whole guarded block that is safe for insertions.
268: */
269: public abstract Position getStartPosition();
270:
271: public abstract void resolvePositions() throws BadLocationException;
272:
273: }
|