2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.editor.guards;
44
import java.io.IOException;
45
import java.io.InputStream;
46
import java.io.OutputStream;
47
import java.io.OutputStreamWriter;
48
import java.io.Reader;
49
import java.io.Writer;
50
import java.nio.charset.Charset;
51
import java.util.ArrayList;
52
import java.util.HashMap;
53
import java.util.List;
56
import java.util.TreeSet;
57
import java.util.logging.Level;
58
import java.util.logging.Logger;
59
import javax.swing.text.BadLocationException;
60
import javax.swing.text.Document;
61
import javax.swing.text.Position;
62
import javax.swing.text.StyledDocument;
63
import org.netbeans.api.editor.guards.GuardedSection;
64
import org.netbeans.api.editor.guards.GuardedSectionManager;
65
import org.netbeans.api.editor.guards.InteriorSection;
66
import org.netbeans.api.editor.guards.SimpleSection;
67
import org.netbeans.spi.editor.guards.GuardedEditorSupport;
68
import org.netbeans.spi.editor.guards.support.AbstractGuardedSectionsProvider;
69
import org.openide.text.NbDocument;
73
* @author Jan Pokorsky
75
public final class GuardedSectionsImpl {
76
/** Table of the guarded sections. Keys are the names of the sections
77
* and values are the GuardedSection classes. The table is null till
78
* while document is not in the memory.
80
Map<String, GuardedSectionImpl> sections = new HashMap<String, GuardedSectionImpl>(10);
82
final GuardedEditorSupport editor;
84
private NewLine newLineType;
86
/** Creates a new instance of GuardedDocument */
87
public GuardedSectionsImpl(GuardedEditorSupport ces) {
91
public Reader createGuardedReader(AbstractGuardedSectionsProvider gr, InputStream stream, Charset encoding) {
92
GuardedReader greader = new GuardedReader(gr, stream, false, encoding, this);
94
Document doc = getDocument();
95
if (doc.getProperty(GuardedSectionManager.class) == null) {
96
GuardedSectionManager api = GuardsAccessor.DEFAULT.createGuardedSections(this);
97
doc.putProperty(GuardedSectionManager.class, api);
102
public Writer createGuardedWriter(AbstractGuardedSectionsProvider gw, OutputStream stream, Charset encoding) {
103
OutputStream os = new NewLineOutputStream(stream, newLineType);
104
if (sections != null) {
105
List<GuardedSection> list = new ArrayList<GuardedSection>(getGuardedSections());
106
if (list.size() > 0) {
107
GuardedWriter writer = new GuardedWriter(gw, os, list, encoding);
112
if (encoding == null)
113
w = new OutputStreamWriter(os);
115
w = new OutputStreamWriter(os, encoding);
120
public StyledDocument getDocument() {
121
return this.editor.getDocument();
124
/** Try to find the section of the given name.
125
* @param name the name of the looked-for section
126
* @return the found guarded section or <code>null</code> if there is no section
129
public GuardedSection findSection(String name) {
130
StyledDocument doc = this.editor.getDocument();
131
synchronized(sections) {
132
GuardedSectionImpl gsi = sections.get(name);
140
public Set<GuardedSection> getGuardedSections() {
141
StyledDocument doc = this.editor.getDocument();
142
synchronized(this.sections) {
143
Set<GuardedSection> sortedGuards = new TreeSet<GuardedSection>(new GuardedPositionComparator());
144
for (GuardedSectionImpl gsi: this.sections.values()) {
145
sortedGuards.add(gsi.guard);
151
public SimpleSection createSimpleSectionObject(String name, PositionBounds bounds) {
152
return (SimpleSection) createSimpleSectionImpl(name, bounds).guard;
155
public InteriorSection createInteriorSectionObject(String name, PositionBounds header, PositionBounds body, PositionBounds footer) {
156
return (InteriorSection) createInteriorSectionImpl(name, header, body, footer).guard;
159
public SimpleSection createSimpleSection(Position pos, String name) throws BadLocationException {
160
checkNewSection(pos, name);
161
return doCreateSimpleSection(pos, name);
164
public InteriorSection createInteriorSection(Position pos, String name) throws BadLocationException {
165
checkNewSection(pos, name);
166
return doCreateInteriorSection(pos, name);
169
private SimpleSection doCreateSimpleSection(final Position pos, final String name)
170
throws /*IllegalArgumentException,*/ BadLocationException {
172
StyledDocument loadedDoc = null;
173
loadedDoc = this.editor.getDocument();
174
final StyledDocument doc = loadedDoc;
175
final SimpleSectionImpl[] sect = new SimpleSectionImpl[1];
176
final BadLocationException[] blex = new BadLocationException[1];
178
NbDocument.runAtomic(doc, new Runnable() {
181
int where = pos.getOffset();
182
doc.insertString(where, "\n \n", null); // NOI18N
183
sect[0] = createSimpleSectionImpl(name, PositionBounds.create(where + 1, where + 2, GuardedSectionsImpl.this));
184
sect[0].markGuarded(doc);
185
} catch (BadLocationException ex) {
191
if (blex[0] == null) {
192
synchronized (this.sections) {
193
sections.put(name, sect[0]);
194
return (SimpleSection) sect[0].guard;
197
throw (BadLocationException) new BadLocationException(
198
"wrong offset", blex[0].offsetRequested() // NOI18N
199
).initCause(blex[0]);
204
/** Create new interior guarded section at a specified place.
205
* @param pos section to create the new one after
206
* @param name the name of the new section
207
* @exception IllegalArgumentException if the name is already in use
208
* @exception BadLocationException if it is not possible to create a
209
* new guarded section here
211
private InteriorSection doCreateInteriorSection(final Position pos,
213
throws IllegalArgumentException, BadLocationException {
214
StyledDocument loadedDoc = null;
215
loadedDoc = this.editor.getDocument();
217
final StyledDocument doc = loadedDoc;
218
final InteriorSectionImpl[] sect = new InteriorSectionImpl[1];
219
final BadLocationException[] blex = new BadLocationException[1];
221
NbDocument.runAtomic(doc, new Runnable() {
224
int where = pos.getOffset();
225
doc.insertString(where, "\n \n \n \n", null); // NOI18N
226
sect[0] = createInteriorSectionImpl(
228
PositionBounds.create(where + 1, where + 2, GuardedSectionsImpl.this),
229
PositionBounds.createBodyBounds(where + 3, where + 4, GuardedSectionsImpl.this),
230
PositionBounds.create(where + 5, where + 6, GuardedSectionsImpl.this)
232
sections.put(sect[0].getName(), sect[0]);
233
sect[0].markGuarded(doc);
234
} catch (BadLocationException ex) {
240
if (blex[0] == null) {
241
synchronized (this.sections) {
242
sections.put(name, sect[0]);
243
return (InteriorSection) sect[0].guard;
246
throw (BadLocationException) new BadLocationException(
247
"wrong offset", blex[0].offsetRequested() // NOI18N
248
).initCause(blex[0]);
254
/** Takes the section descriptors from the GuardedReader and
255
* fills the table 'sections', also marks as guarded all sections
256
* in the given document.
257
* @param is Where to take the guarded section descriptions.
258
* @param doc Where to mark guarded.
260
void fillSections(List<GuardedSection> l, NewLine newLineType) {
261
this.newLineType = newLineType;
262
// XXX this should invalidate removed GS instances
263
// XXX maybe would be useful to map new list to old list to keep track of valid instances as much as possible
265
this.sections.clear();
267
for (GuardedSection gs: l) {
269
GuardedSectionImpl gsi = GuardsAccessor.DEFAULT.getImpl(gs);
270
gsi.resolvePositions();
271
sections.put(gs.getName(), gsi);
272
StyledDocument doc = getDocument();
273
gsi.markGuarded(doc);
274
} catch (BadLocationException ex) {
275
Logger.getLogger(GuardedSectionsImpl.class.getName()).log(Level.SEVERE, ex.getLocalizedMessage(), ex);
282
private SimpleSectionImpl createSimpleSectionImpl(String name, PositionBounds bounds) {
283
SimpleSectionImpl sect = new SimpleSectionImpl(name, bounds, this);
284
GuardsAccessor.DEFAULT.createSimpleSection(sect);
288
private InteriorSectionImpl createInteriorSectionImpl(
289
String name, PositionBounds header, PositionBounds body, PositionBounds footer) {
291
InteriorSectionImpl sect;
292
sect = new InteriorSectionImpl(name, header, body, footer, this);
293
GuardsAccessor.DEFAULT.createInteriorSection(sect);
297
private void checkNewSection(Position p, String name) {
298
synchronized (sections) {
300
GuardedSectionImpl gs = sections.get(name);
302
throw new IllegalArgumentException("name exists"); // NOI18N
307
private void checkOverlap(Position p) throws IllegalArgumentException {
308
for (GuardedSectionImpl gs: this.sections.values()) {
309
if (gs.contains(p, false))
310
throw new IllegalArgumentException("Sections overlap"); // NOI18N
314
/** This stream is used for changing the new line delimiters.
315
* It replaces the '\n' by '\n', '\r' or "\r\n"
317
private static class NewLineOutputStream extends OutputStream {
318
/** Underlying stream. */
321
/** The type of new line delimiter */
324
/** Creates new stream.
325
* @param stream Underlaying stream
326
* @param newLineType The type of new line delimiter
328
public NewLineOutputStream(OutputStream stream, NewLine newLineType) {
329
this.stream = stream;
330
this.newLineType = newLineType;
333
/** Write one character.
334
* @param b char to write.
336
public void write(int b) throws IOException {
338
switch (newLineType) {
353
public void close() throws IOException {
358
public void flush() throws IOException {