This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 41345
Collapse All | Expand All

(-)openide-spec-vers.properties (-1 / +1 lines)
Lines 4-7 Link Here
4
# Must always be numeric (numbers separated by '.', e.g. 4.11).
4
# Must always be numeric (numbers separated by '.', e.g. 4.11).
5
# See http://openide.netbeans.org/versioning-policy.html for more.
5
# See http://openide.netbeans.org/versioning-policy.html for more.
6
6
7
openide.specification.version=4.29
7
openide.specification.version=4.30
(-)api/doc/changes/apichanges.xml (+19 lines)
Lines 116-121 Link Here
116
116
117
  <changes>
117
  <changes>
118
    <change>
118
    <change>
119
    <api name="editor"/>
120
    <summary>Support for AnnotationProvider interface</summary>
121
    <version major="4" minor="30"/>
122
    <date day="30" month="4" year="2004"/>
123
    <author login="pnejedly"/>
124
    <compatibility addition="yes"/>
125
    <description>
126
        <ul>
127
            <li>Added interface org.openide.text.AnnotationProvider</li>
128
            <li>Call all instances of the interface found in the global lookup
129
	      when going to visualize given Annotatable.
130
	    </li>
131
        </ul>
132
    </description>
133
    <class package="org.openide.text" name="AnnotationProvider"/>
134
    <issue number="41345"/>
135
    </change>
136
137
    <change>
119
    <api name="filesystems"/>
138
    <api name="filesystems"/>
120
    <summary>There should exist just one FileObject for one resource (java.io.File or URL).  </summary>
139
    <summary>There should exist just one FileObject for one resource (java.io.File or URL).  </summary>
121
    <version major="4" minor="29"/>
140
    <version major="4" minor="29"/>
(-)src/org/openide/text/AnnotationProvider.java (+39 lines)
Added Link Here
1
/*
2
 *                 Sun Public License Notice
3
 *
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 *
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
14
package org.openide.text;
15
16
import org.openide.util.Lookup;
17
18
/**
19
 * A provider of annotations for given context.
20
 *
21
 * Implementations of this interface are looked up in the global lookup
22
 * and called to let them attach annotations to the lines in the set.
23
 * The call is performed during opening of given context.
24
 *
25
 * @author Petr Nejedly
26
 * @since 4.30
27
 */
28
public interface AnnotationProvider {
29
30
    /**
31
     * Attach annotations to the Line.Set for given context.
32
     *
33
     * @param set the Line.Set to attach annotations to.
34
     * @param context a Lookup describing the context for the Line.Set.
35
     *  it shall contain the FileObject the LineSet is associated with.
36
     */
37
    public void annotate (Line.Set set, Lookup context); 
38
        
39
}
(-)src/org/openide/text/CloneableEditor.java (+2 lines)
Lines 212-217 Link Here
212
                caret.setDot(cursorPosition);
212
                caret.setDot(cursorPosition);
213
            }
213
            }
214
        }
214
        }
215
	
216
	support.ensureAnnotationsLoaded();
215
    }
217
    }
216
218
217
    protected CloneableTopComponent createClonedObject() {
219
    protected CloneableTopComponent createClonedObject() {
(-)src/org/openide/text/CloneableEditorSupport.java (+14 lines)
Lines 286-291 Link Here
286
                
286
                
287
        return positionManager;
287
        return positionManager;
288
    }
288
    }
289
290
    private boolean annotationsLoaded;
291
292
    void ensureAnnotationsLoaded() {
293
        if (!annotationsLoaded) {
294
	    annotationsLoaded = true;
295
            Line.Set lines = getLineSet();
296
	    Lookup.Result result = Lookup.getDefault().lookup(new Lookup.Template(AnnotationProvider.class));
297
            for (Iterator it = result.allInstances().iterator(); it.hasNext(); ) {
298
                AnnotationProvider act = (AnnotationProvider)it.next();
299
                act.annotate(lines, lookup);
300
            }
301
	}
302
    }
289
    
303
    
290
    /** Overrides superclass method, first processes document preparation.
304
    /** Overrides superclass method, first processes document preparation.
291
     * @see #prepareDocument */
305
     * @see #prepareDocument */
(-)loaders/src/org/openide/text/DataEditorSupport.java (-1 / +26 lines)
Lines 33-38 Link Here
33
import org.openide.util.Mutex;
33
import org.openide.util.Mutex;
34
import org.openide.windows.*;
34
import org.openide.windows.*;
35
import org.openide.util.NbBundle;
35
import org.openide.util.NbBundle;
36
import org.openide.util.Lookup;
37
import org.openide.util.lookup.*;
36
38
37
/** Support for associating an editor and a Swing {@link Document} to a data object.
39
/** Support for associating an editor and a Swing {@link Document} to a data object.
38
* 
40
* 
Lines 51-57 Link Here
51
    * @param env environment to pass to 
53
    * @param env environment to pass to 
52
    */
54
    */
53
    public DataEditorSupport (DataObject obj, CloneableEditorSupport.Env env) {
55
    public DataEditorSupport (DataObject obj, CloneableEditorSupport.Env env) {
54
        super (env, org.openide.util.lookup.Lookups.singleton (obj));
56
        super (env, createLookup(obj));
55
        this.obj = obj;
57
        this.obj = obj;
56
    }
58
    }
57
    
59
    
Lines 588-592 Link Here
588
        }
590
        }
589
        
591
        
590
    } // end of DataNodeListener
592
    } // end of DataNodeListener
593
    
594
    /* Create a special lookup implementation that contains a DataObject and its
595
     * primary fileobject. If the file is moved, the FileObject is replaced,
596
     * while the DataObject keeps the identity.
597
     */
598
    private static Lookup createLookup(final DataObject dobj) {
599
	final InstanceContent ic = new InstanceContent();
600
	Lookup l = new AbstractLookup(ic);
601
	dobj.addPropertyChangeListener(new PropertyChangeListener() {
602
	    public void propertyChange(PropertyChangeEvent ev) {
603
		String propName = ev.getPropertyName();
604
		if (propName == null || propName == DataObject.PROP_PRIMARY_FILE) {
605
		    updateLookup(dobj, ic);
606
		}
607
	    }
608
	});
609
	updateLookup(dobj,ic);
610
	return l;
611
    }
612
    
613
    private static void updateLookup(DataObject d, InstanceContent ic) {
614
	ic.set(Arrays.asList(new Object[] { d, d.getPrimaryFile() }), null);
615
    }
591
    
616
    
592
}
617
}
(-)test/unit/src/org/openide/text/AnnotationProviderTest.java (+453 lines)
Added Link Here
1
/*
2
 *                 Sun Public License Notice
3
 * 
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 * 
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
14
15
package org.openide.text;
16
17
18
import java.io.File;
19
import java.io.IOException;
20
import java.lang.reflect.Method;
21
import java.util.Collections;
22
import javax.swing.SwingUtilities;
23
import javax.swing.text.BadLocationException;
24
import javax.swing.text.Position;
25
import javax.swing.text.StyledDocument;
26
27
import junit.textui.TestRunner;
28
29
import org.netbeans.junit.NbTestCase;
30
import org.netbeans.junit.NbTestSuite;
31
import org.openide.actions.*;
32
import org.openide.cookies.CloseCookie;
33
import org.openide.cookies.EditCookie;
34
35
import org.openide.cookies.EditorCookie;
36
import org.openide.cookies.OpenCookie;
37
import org.openide.cookies.PrintCookie;
38
import org.openide.cookies.SaveCookie;
39
import org.openide.filesystems.FileLock;
40
import org.openide.filesystems.FileObject;
41
import org.openide.filesystems.FileStateInvalidException;
42
import org.openide.filesystems.FileSystem;
43
import org.openide.filesystems.Repository;
44
import org.openide.filesystems.TestUtilHid;
45
import org.openide.loaders.DataNode;
46
import org.openide.loaders.DataObject;
47
import org.openide.loaders.DataObjectExistsException;
48
import org.openide.loaders.ExtensionList;
49
import org.openide.loaders.MultiDataObject;
50
import org.openide.loaders.MultiFileLoader;
51
import org.openide.loaders.UniFileLoader;
52
import org.openide.nodes.Children;
53
import org.openide.nodes.CookieSet;
54
import org.openide.nodes.Node;
55
import org.openide.text.CloneableEditorSupport;
56
import org.openide.util.HelpCtx;
57
import org.openide.util.Lookup;
58
import org.openide.util.NbBundle;
59
import org.openide.util.actions.SystemAction;
60
import org.openide.windows.CloneableOpenSupport;
61
import org.openide.windows.WindowManager;
62
63
64
/**
65
 */
66
public class AnnotationProviderTest extends NbTestCase {
67
    
68
    public AnnotationProviderTest(String s) {
69
        super(s);
70
    }
71
    
72
    public static void main(String[] args) {
73
        TestRunner.run(new NbTestSuite(AnnotationProviderTest.class));
74
    }
75
76
    private FileSystem fs;
77
    
78
    protected void setUp() throws Exception {
79
        System.setProperty("org.openide.util.Lookup", "org.openide.text.AnnotationProviderTest$Lkp");
80
        
81
        TestUtilHid.destroyLocalFileSystem (getName());
82
        fs = TestUtilHid.createLocalFileSystem (getName(), new String[] {});
83
    }
84
    
85
    protected void tearDown() throws Exception {
86
    }
87
88
    public void testLookupIsConsistent() throws Exception {              
89
        Object o = Lookup.getDefault().lookup(AnnotationProvider.class);
90
        if(o == null) {
91
            fail("No  annotation provider found");
92
        }
93
        
94
        FileObject fo = fs.getRoot().createData("test", "txt");
95
96
        DataObject data = DataObject.find(fo);
97
        
98
        EditorCookie ec = (EditorCookie)data.getCookie(EditorCookie.class);
99
        
100
        if(!(ec instanceof CloneableEditorSupport)) {
101
            fail("Bad editor cookie type");
102
        }
103
104
        CloneableEditorSupport ces = (CloneableEditorSupport)ec;
105
106
        ConsistencyCheckProvider.called = 0;
107
        ces.open();
108
        
109
        assertEquals("Provider called exactly once", 1, ConsistencyCheckProvider.called);
110
        assertEquals("Consistent lookup content", data.getPrimaryFile(), ConsistencyCheckProvider.inLkp);
111
        
112
        assertEquals("Exactly one annotation attached", 1, ces.getLineSet().getCurrent(0).getAnnotationCount());
113
        
114
        ces.close();
115
        assertEquals("Exactly one annotation attached after close", 1, ces.getLineSet().getCurrent(0).getAnnotationCount());
116
117
        ConsistencyCheckProvider.called = 0;
118
        ces.open();
119
        assertEquals("Provider called exactly once during reopen", 1, ConsistencyCheckProvider.called);
120
        assertEquals("Exactly one annotation attached after reopen", 1, ces.getLineSet().getCurrent(0).getAnnotationCount());
121
122
    }
123
    public static class ConsistencyCheckProvider implements AnnotationProvider {
124
        
125
        private static int called;
126
        private static FileObject inLkp;
127
        
128
        public void annotate(org.openide.text.Line.Set set, org.openide.util.Lookup context) {
129
            inLkp= (FileObject)context.lookup(FileObject.class);
130
            called++;
131
            
132
            set.getCurrent(0).addAnnotation(new MyAnnotation());
133
        }        
134
        
135
    }
136
137
    
138
    // below is only irrelevant support stuff
139
    
140
    private static class MyAnnotation extends Annotation {
141
        
142
        public String getAnnotationType() {
143
            return "nowhere";
144
        }
145
        
146
        public String getShortDescription() {
147
            return "Test annotation";
148
        }
149
        
150
    }
151
    
152
    protected boolean runInEQ() {
153
        return true;
154
    }    
155
    
156
    //
157
    // Code from text module
158
    // 
159
    
160
161
    public static final class TXTDataLoader extends UniFileLoader {
162
163
        /** Generated serial version UID. */
164
        static final long serialVersionUID =-3658061894653334886L;    
165
166
        /** file attribute which forces a file to be considered a text file */
167
        static final String ATTR_IS_TEXT_FILE = "org.netbeans.modules.text.IsTextFile"; // NOI18N
168
169
170
        /** Creates new <code>TXTDataLoader</code>. */
171
        public TXTDataLoader() {
172
            super("org.netbeans.modules.text.TXTDataObject"); // NOI18N
173
        }
174
175
        /** Does initialization. Initializes extension list. */
176
        protected void initialize () {
177
            super.initialize();
178
179
            ExtensionList ext = new ExtensionList();
180
            ext.addExtension("txt"); // NOI18N
181
            ext.addExtension("doc"); // NOI18N
182
            ext.addExtension("me"); // for read.me files // NOI18N
183
            ext.addExtension("policy"); // NOI18N
184
            ext.addExtension("mf"); // for manifest.mf files // NOI18N
185
            ext.addExtension("MF"); //  -""- // NOI18N
186
            ext.addExtension("log"); // log files are nice to be readable // NOI18N
187
            setExtensions(ext);
188
        }
189
190
        /** Gets default display name. Overrides superclass method. */
191
        protected String defaultDisplayName() {
192
            return NbBundle.getBundle(TXTDataLoader.class).getString("PROP_TXTLoader_Name");
193
        }
194
195
        /** Gets default system actions. Overrides superclass method. */
196
        protected SystemAction[] defaultActions() {
197
            return new SystemAction[] {
198
                SystemAction.get(OpenAction.class),
199
                SystemAction.get (FileSystemAction.class),
200
                null,
201
                SystemAction.get(CutAction.class),
202
                SystemAction.get(CopyAction.class),
203
                SystemAction.get(PasteAction.class),
204
                null,
205
                SystemAction.get(DeleteAction.class),
206
                SystemAction.get(RenameAction.class),
207
                null,
208
                SystemAction.get(SaveAsTemplateAction.class),
209
                null,
210
                SystemAction.get(ToolsAction.class),
211
                SystemAction.get(PropertiesAction.class),
212
            };
213
        }
214
215
        /** Check whether a file is recognized.
216
         * It will be if the extension matches, or if it is marked to be a text file. */
217
        protected FileObject findPrimaryFile (FileObject fo) {
218
            boolean isSysFile;
219
            try {
220
                isSysFile = fo.getFileSystem () == Repository.getDefault ().getDefaultFileSystem ();
221
            } catch (FileStateInvalidException fsie) {
222
                // Never mind.
223
                isSysFile = false;
224
            }
225
            if (! isSysFile && Boolean.TRUE.equals (fo.getAttribute (ATTR_IS_TEXT_FILE)))
226
                return fo;
227
            return super.findPrimaryFile (fo);
228
        }
229
230
        /** Creates new <code>TXTDataObject</code> for specified <code>FileObject</code>.
231
         * @param fo FileObject
232
         * @return new TXTDataObject
233
         */
234
        protected MultiDataObject createMultiObject(final FileObject fo)
235
        throws IOException {
236
            return new TXTDataObject(fo, this);
237
        }
238
239
    } // end of TXTDataLoader
240
241
    
242
    public static final class TXTDataObject extends MultiDataObject implements CookieSet.Factory {
243
244
        /** Generated Serialized Version UID */
245
        static final long serialVersionUID = 4795737295255253334L;
246
247
        /** Editor support for text data object. */
248
        private transient TXTEditorSupport editorSupport;
249
250
251
        /** Constructor. */
252
        public TXTDataObject(final FileObject obj, final MultiFileLoader loader) throws DataObjectExistsException {
253
            super(obj, loader);
254
255
            getCookieSet().add(TXTEditorSupport.class, this);
256
        }
257
258
259
        /** Implements <code>CookieSet.Factory</code> interface. */
260
        public Node.Cookie createCookie(Class clazz) {
261
            if(clazz.isAssignableFrom(TXTEditorSupport.class))
262
                return getEditorSupport();
263
            else
264
                return null;
265
        }
266
267
        // Accessibility from TXTEditorSupport:
268
        CookieSet getCookieSet0() {
269
            return getCookieSet();
270
        }
271
272
        /** Gets editor support for this data object. */
273
        private TXTEditorSupport getEditorSupport() {
274
            if(editorSupport == null) {
275
                synchronized(this) {
276
                    if(editorSupport == null)
277
                        editorSupport = new TXTEditorSupport(this);
278
                }
279
            }
280
281
            return editorSupport;
282
        }
283
284
        /** Provides node that should represent this data object. When a node for representation
285
         * in a parent is requested by a call to getNode (parent) it is the exact copy of this node
286
         * with only parent changed. This implementation creates instance <code>DataNode</code>.
287
         * <p>
288
         * This method is called only once.
289
         *
290
         * @return the node representation for this data object
291
         * @see DataNode
292
         */
293
        protected Node createNodeDelegate () {
294
            return new TXTNode(this);
295
        }
296
297
        /** Help context for this object.
298
         * @return help context
299
         */
300
        public HelpCtx getHelpCtx () {
301
            return new HelpCtx (TXTDataObject.class);
302
        }
303
304
305
        /** Text node implementation.
306
         * Leaf node, default action opens editor or instantiates template.
307
         * Icons redefined.
308
         */
309
        public static final class TXTNode extends DataNode {
310
            /** Icon base for the TXTNode node */
311
            private static final String TXT_ICON_BASE = "org/netbeans/modules/text/txtObject"; // NOI18N
312
313
            /** Constructs node. */
314
            public TXTNode (final DataObject dataObject) {
315
                super(dataObject, Children.LEAF);
316
                setIconBase(TXT_ICON_BASE);
317
            }
318
319
            /** Overrides default action from DataNode. */
320
            public SystemAction getDefaultAction () {
321
                SystemAction result = super.getDefaultAction();
322
                return result == null ? SystemAction.get(OpenAction.class) : result;
323
            }
324
        } // End of nested class TXTNode.
325
326
    } // TXTDataObject
327
    
328
    
329
    public static final class TXTEditorSupport extends DataEditorSupport
330
    implements OpenCookie, EditCookie, EditorCookie.Observable, PrintCookie, CloseCookie {
331
332
        /** SaveCookie for this support instance. The cookie is adding/removing 
333
         * data object's cookie set depending on if modification flag was set/unset. */
334
        private final SaveCookie saveCookie = new SaveCookie() {
335
            /** Implements <code>SaveCookie</code> interface. */
336
            public void save() throws IOException {
337
                TXTEditorSupport.this.saveDocument();
338
                TXTEditorSupport.this.getDataObject().setModified(false);
339
            }
340
        };
341
342
343
        /** Constructor. */
344
        TXTEditorSupport(TXTDataObject obj) {
345
            super(obj, new Environment(obj));
346
347
            setMIMEType("text/plain"); // NOI18N
348
        }
349
350
        /** 
351
         * Overrides superclass method. Adds adding of save cookie if the document has been marked modified.
352
         * @return true if the environment accepted being marked as modified
353
         *    or false if it has refused and the document should remain unmodified
354
         */
355
        protected boolean notifyModified () {
356
            if (!super.notifyModified()) 
357
                return false;
358
359
            addSaveCookie();
360
361
            return true;
362
        }
363
364
        /** Overrides superclass method. Adds removing of save cookie. */
365
        protected void notifyUnmodified () {
366
            super.notifyUnmodified();
367
368
            removeSaveCookie();
369
        }
370
371
        /** Helper method. Adds save cookie to the data object. */
372
        private void addSaveCookie() {
373
            TXTDataObject obj = (TXTDataObject)getDataObject();
374
375
            // Adds save cookie to the data object.
376
            if(obj.getCookie(SaveCookie.class) == null) {
377
                obj.getCookieSet0().add(saveCookie);
378
                obj.setModified(true);
379
            }
380
        }
381
382
        /** Helper method. Removes save cookie from the data object. */
383
        private void removeSaveCookie() {
384
            TXTDataObject obj = (TXTDataObject)getDataObject();
385
386
            // Remove save cookie from the data object.
387
            Node.Cookie cookie = obj.getCookie(SaveCookie.class);
388
389
            if(cookie != null && cookie.equals(saveCookie)) {
390
                obj.getCookieSet0().remove(saveCookie);
391
                obj.setModified(false);
392
            }
393
        }
394
395
396
        /** Nested class. Environment for this support. Extends
397
         * <code>DataEditorSupport.Env</code> abstract class.
398
         */
399
400
        private static class Environment extends DataEditorSupport.Env
401
        {
402
            private static final long serialVersionUID = 3499855082262173256L;
403
404
            /** Constructor. */
405
            public Environment(TXTDataObject obj) {
406
                super(obj);
407
            }
408
409
410
            /** Implements abstract superclass method. */
411
            protected FileObject getFile() {
412
                return getDataObject().getPrimaryFile();
413
            }
414
415
            /** Implements abstract superclass method.*/
416
            protected FileLock takeLock() throws IOException {
417
                return ((TXTDataObject)getDataObject()).getPrimaryEntry().takeLock();
418
            }
419
420
            /** 
421
             * Overrides superclass method.
422
             * @return text editor support (instance of enclosing class)
423
             */
424
            public CloneableOpenSupport findCloneableOpenSupport() {
425
                return (TXTEditorSupport)getDataObject().getCookie(TXTEditorSupport.class);
426
            }
427
        } // End of nested Environment class.
428
429
    } // TXTEditorSupport
430
431
    
432
    private static class MyPool extends org.openide.loaders.DataLoaderPool {
433
        protected java.util.Enumeration loaders() {
434
            return Collections.enumeration(Collections.singleton(
435
                TXTDataLoader.getLoader(TXTDataLoader.class)
436
            ));
437
        }
438
        
439
    }
440
    
441
    public static class Lkp extends org.openide.util.lookup.AbstractLookup {
442
        public Lkp () {
443
            this (new org.openide.util.lookup.InstanceContent ());
444
        }
445
        
446
        private Lkp (org.openide.util.lookup.InstanceContent ic) {
447
            super (ic);
448
            
449
            ic.add (new MyPool ());
450
            ic.add (new ConsistencyCheckProvider ());
451
        }
452
    }
453
}

Return to bug 41345