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 20962
Collapse All | Expand All

(-)openide/api/doc/changes/apichanges.xml (+21 lines)
Lines 114-119 Link Here
114
114
115
<!-- ACTUAL CHANGES BEGIN HERE: -->
115
<!-- ACTUAL CHANGES BEGIN HERE: -->
116
<changes>
116
<changes>
117
    <change id="SharedClassObject.reset">
118
        <api name="util"/>
119
        <summary>Added <code>SharedClassObject.reset</code> method to allow subclasses to implement reset correctly</summary>
120
        <version major="4" minor="46"/>
121
        <date day="2" month="9" year="2004"/>
122
        <author login="jtulach"/>
123
        <compatibility addition="yes" binary="compatible" semantic="compatible" />
124
        <description>
125
            The new <code>SharedClassObject.reset</code> method is called
126
            by the infrastructure in moments when an original (at the time
127
            of start) state of an option or any other <code>SharedClassObject</code>
128
            is requested. Interested subclasses are free to implement any kind of clean
129
            then need. The <code>SystemOption</code> provides a default 
130
            implementation based on fired property changed events, so 
131
            its subclasses usually do not need to do anything.
132
        </description>
133
        <class package="org.openide.util" name="SharedClassObject"/>
134
        <class package="org.openide.options" name="SystemOption"/>
135
        <issue number="20962"/>
136
    </change>
137
    
117
    <change id="TreeView.dragActive">
138
    <change id="TreeView.dragActive">
118
        <api name="explorer"/>
139
        <api name="explorer"/>
119
        <summary>Fixed <code>TreeView.drag/dropActive</code> switcher</summary>
140
        <summary>Fixed <code>TreeView.drag/dropActive</code> switcher</summary>
(-)openide/src/org/openide/options/SystemOption.java (-3 / +84 lines)
Lines 22-27 Link Here
22
import java.lang.reflect.InvocationTargetException;
22
import java.lang.reflect.InvocationTargetException;
23
import java.lang.reflect.Method;
23
import java.lang.reflect.Method;
24
import java.util.HashMap;
24
import java.util.HashMap;
25
import java.util.Map;
25
import org.openide.ErrorManager;
26
import org.openide.ErrorManager;
26
import org.openide.util.HelpCtx;
27
import org.openide.util.HelpCtx;
27
import org.openide.util.NbBundle;
28
import org.openide.util.NbBundle;
Lines 50-61 Link Here
50
    private static final Object PROP_LOADING = new Object ();
51
    private static final Object PROP_LOADING = new Object ();
51
    /** property to indicate that the option is currently loading its data */
52
    /** property to indicate that the option is currently loading its data */
52
    private static final Object PROP_STORING = new Object ();
53
    private static final Object PROP_STORING = new Object ();
54
    /** property that holds a Map<String,Object> that stores old values */
55
    private static final Object PROP_ORIGINAL_VALUES = new Object ();
53
56
54
    /** Default constructor. */
57
    /** Default constructor. */
55
    public SystemOption() {
58
    public SystemOption() {
56
        // SystemOption must declare this property in order to be correctly deserialized
57
        // by SharedClassObject.findObject function
58
        putProperty ("netbeans.systemoption.hack", null); // NOI18N
59
    }
59
    }
60
60
61
    /** Fire a property change event to all listeners. Delays
61
    /** Fire a property change event to all listeners. Delays
Lines 68-73 Link Here
68
    protected void firePropertyChange (
68
    protected void firePropertyChange (
69
        String name, Object oldValue, Object newValue
69
        String name, Object oldValue, Object newValue
70
    ) {
70
    ) {
71
        if (name != null && oldValue != null) {
72
            Map originalValues = (Map)getProperty (PROP_ORIGINAL_VALUES);
73
            if (originalValues == null) {
74
                originalValues = new HashMap ();
75
                putProperty (PROP_ORIGINAL_VALUES, originalValues);
76
            }
77
            if (originalValues.get (name) == null) {
78
                if (getProperty (name) == null) {
79
                    // this is supposed to be setter
80
                    originalValues.put (name, new Box (oldValue));
81
                } else {
82
                    // regular usage of putProperty (....);
83
                    originalValues.put (name, oldValue);
84
                }
85
            }
86
        }
87
        
71
        if (getProperty (PROP_LOADING) != null) {
88
        if (getProperty (PROP_LOADING) != null) {
72
            // somebody is loading, assign any object different than
89
            // somebody is loading, assign any object different than
73
            // this to indicate that firing should occure
90
            // this to indicate that firing should occure
Lines 78-83 Link Here
78
        super.firePropertyChange (name, oldValue, newValue);
95
        super.firePropertyChange (name, oldValue, newValue);
79
    }
96
    }
80
97
98
    /** Implements the reset by setting back all properties that were 
99
     * modified. A <em>modified property</em> has fired a 
100
     * <code>PropertyChangeEvent</code> with
101
     * non-null name and non-null old value. The name and value are 
102
     * remembered and this method sets them back to original value.
103
     * <p>
104
     * Subclasses are free to override this method and reimplement the
105
     * reset by themselves.
106
     *
107
     * @since 4.46
108
     */
109
    protected void reset () {
110
        synchronized (getLock ()) {
111
            Map m = (Map)getProperty (PROP_ORIGINAL_VALUES);
112
            if (m == null || m.isEmpty ()) return;
113
            
114
            java.util.Iterator it = m.entrySet ().iterator ();
115
            WHILE: while (it.hasNext ()) {
116
                Map.Entry e = (Map.Entry)it.next ();
117
                if (e.getValue () instanceof Box) {
118
                    Object value = ((Box)e.getValue ()).value;
119
                    try {
120
                        // gets info about all properties that were added by subclass
121
                        BeanInfo info = org.openide.util.Utilities.getBeanInfo (getClass (), SystemOption.class);
122
                        PropertyDescriptor[] desc = info.getPropertyDescriptors ();
123
                        
124
                        for (int i = 0; i < desc.length; i++) {
125
                            if (e.getKey ().equals (desc[i].getName ())) {
126
                                // our property
127
                                Method write = desc[i].getWriteMethod ();
128
                                if (write != null) {
129
                                    write.invoke (this, new Object[] { value });
130
                                }
131
                                continue WHILE;
132
                            }
133
                        }
134
                    } catch (InvocationTargetException ex) {
135
                        // exception thrown
136
                        ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex);
137
                    } catch (IllegalAccessException ex) {
138
                        ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex);
139
                    } catch (IntrospectionException ex) {
140
                        ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex);
141
                    }
142
                } else {
143
                    putProperty (e.getKey (), e.getValue ());
144
                }
145
            }
146
            // reset all remembered values
147
            putProperty (PROP_ORIGINAL_VALUES, null);
148
        }
149
        super.firePropertyChange (null, null, null);
150
    }
151
    
81
    /** Write all properties of this object (or subclasses) to an object output.
152
    /** Write all properties of this object (or subclasses) to an object output.
82
    * @param out the output stream
153
    * @param out the output stream
83
    * @exception IOException on error
154
    * @exception IOException on error
Lines 328-331 Link Here
328
    protected final boolean isWriteExternal () {
399
    protected final boolean isWriteExternal () {
329
        return getProperty (PROP_STORING) != null;
400
        return getProperty (PROP_STORING) != null;
330
    }
401
    }
402
    
403
    /** A wrapper object to indicate that a setter should be called 
404
     * when reseting to default.
405
     */
406
    private static final class Box extends Object {
407
        public Object value;
408
        public Box (Object v) {
409
            this.value = v;
410
        }
411
    } // end of Box
331
}
412
}
(-)openide/src/org/openide/util/SharedClassObject.java (-58 / +24 lines)
Lines 40-47 Link Here
40
    /** Name of the method used to determine whether an option is global or not. */
40
    /** Name of the method used to determine whether an option is global or not. */
41
    static final String GLOBAL_METHOD_NAME = "isGlobal"; // NOI18N
41
    static final String GLOBAL_METHOD_NAME = "isGlobal"; // NOI18N
42
    
42
    
43
    private byte [] defaultInstance = null;
44
    
45
    /** property change support (PropertyChangeSupport) */
43
    /** property change support (PropertyChangeSupport) */
46
    private static final Object PROP_SUPPORT = new Object ();
44
    private static final Object PROP_SUPPORT = new Object ();
47
45
Lines 237-246 Link Here
237
            throw new NullPointerException("Tried to pass null key (value=" + value + ") to putProperty"); // NOI18N
235
            throw new NullPointerException("Tried to pass null key (value=" + value + ") to putProperty"); // NOI18N
238
        }
236
        }
239
        synchronized (getLock ()) {
237
        synchronized (getLock ()) {
240
            if (key.equals ("netbeans.systemoption.hack")) { // NOI18N
241
                systemOption = true;
242
                return null;
243
            }
244
            if (waitingOnSystemOption && key != PROP_SUPPORT &&
238
            if (waitingOnSystemOption && key != PROP_SUPPORT &&
245
                    prematureSystemOptionMutation == null && !dataEntry.isInInitialize() && !inReadExternal) {
239
                    prematureSystemOptionMutation == null && !dataEntry.isInInitialize() && !inReadExternal) {
246
                // See below in findObject. Note that if we are still in initialize(),
240
                // See below in findObject. Note that if we are still in initialize(),
Lines 472-479 Link Here
472
                }
466
                }
473
            }
467
            }
474
            if (created) {
468
            if (created) {
475
                obj.reset ();
476
477
                // This hack was created due to the remove of SystemOptions deserialization
469
                // This hack was created due to the remove of SystemOptions deserialization
478
                // from project open operation, all SystemOptions are deserialized at this place
470
                // from project open operation, all SystemOptions are deserialized at this place
479
                // the first time anybody asks for the option.
471
                // the first time anybody asks for the option.
Lines 481-487 Link Here
481
                // otherwise it can cause deadlocks.
473
                // otherwise it can cause deadlocks.
482
                // Lookup in the active session is used to find serialized state of the option,
474
                // Lookup in the active session is used to find serialized state of the option,
483
                // if such state exists it is deserialized before the object is returned from lookup.
475
                // if such state exists it is deserialized before the object is returned from lookup.
484
                if (obj.systemOption) {
476
                if (obj.isSystemOption ()) {
485
                    // Lookup will find serialized version of searched object and deserialize it
477
                    // Lookup will find serialized version of searched object and deserialize it
486
                    final Lookup.Result r = Lookup.getDefault().lookup(new Lookup.Template(clazz));
478
                    final Lookup.Result r = Lookup.getDefault().lookup(new Lookup.Template(clazz));
487
                    if (r.allInstances().isEmpty()) {
479
                    if (r.allInstances().isEmpty()) {
Lines 518-523 Link Here
518
            return obj;
510
            return obj;
519
        }
511
        }
520
    }
512
    }
513
    
514
    /** checks whether we are instance of system option.
515
     */
516
    private boolean isSystemOption () {
517
        Class c = this.getClass ();
518
        while (c != SharedClassObject.class) {
519
            if ("org.openide.options.SystemOption".equals (c.getName ())) return true; // NOI18N
520
            c = c.getSuperclass ();
521
        }
522
        return false;
523
    }
524
    
521
    // See above:
525
    // See above:
522
    private static void warn(Throwable t) {
526
    private static void warn(Throwable t) {
523
        err.notify(ErrorManager.INFORMATIONAL, t);
527
        err.notify(ErrorManager.INFORMATIONAL, t);
Lines 547-603 Link Here
547
        }
551
        }
548
    }
552
    }
549
553
550
    /** Resets shared data to it default value. */
554
    /** Is called by the infrastructure in cases when a clean instance is requested.
551
    private void reset () {
555
     * As instances of <code>SharedClassObject</code> are singletons, there is 
552
        if (!systemOption || !isProjectOption ()) {
556
     * no way how to create new instance that would not contain the same data
553
            return;
557
     * as previously existing one. This method allows all subclasses that care
554
        }
558
     * about the ability to refresh the settings (like <code>SystemOption</code>s) 
555
559
     * to be notified about the cleaning request and clean their settings themselves.
556
        synchronized (getLock ()) {
560
     * <p>
557
            // [PENDING] should be changed to next line after all options in layers will
561
     * Default implementation does nothing.
558
            // use put{get}Property and initilaize properly
562
     *
559
            // dataEntry.reset (this);
563
     * @since made protected in version 4.46
560
            
561
            if (defaultInstance == null) {
562
                try {
563
                    ByteArrayOutputStream baos = new ByteArrayOutputStream (1024);
564
                    ObjectOutput oo = new org.openide.util.io.NbObjectOutputStream (baos);
565
                    oo.writeObject (this);
566
                    defaultInstance = baos.toByteArray ();
567
                } catch (IOException e) {
568
                    defaultInstance = null;
569
                }
570
                return;
571
            }
572
573
            try {
574
                ByteArrayInputStream bais = new ByteArrayInputStream (defaultInstance);
575
                ObjectInputStream oi = new org.openide.util.io.NbObjectInputStream (bais);
576
                oi.readObject ();
577
            } catch (Exception e) {
578
                // ignore and leave it as it is
579
            }
580
        }
581
    }
582
    
583
    /**
584
     * Test if the object is Project specific.
585
     * @return true if the object is Project specific
586
     */
564
     */
587
    private boolean isProjectOption () {
565
    protected void reset () {
588
        try {
589
            Class clazz = getClass ();
590
            // the old hack with undocumented method isGlobal
591
            Method m = clazz.getMethod(GLOBAL_METHOD_NAME, new Class[] {});
592
            m.setAccessible(true);
593
            Boolean b = (Boolean) m.invoke(this, new Object[] {});
594
            return !b.booleanValue();
595
        } catch (Exception ex) {
596
            // ignore and return default
597
        }
598
        return false;
599
    }
566
    }
600
    
601
    
567
    
602
    /** Class that is used as default write replace.
568
    /** Class that is used as default write replace.
603
    */
569
    */
(-)openide/test/unit/src/org/openide/loaders/InstanceDataObjectTest.java (+54 lines)
Lines 564-569 Link Here
564
        assertNotNull ("Has cookie", ic);
564
        assertNotNull ("Has cookie", ic);
565
        assertEquals ("And its value is x", x, ic.instanceCreate ());
565
        assertEquals ("And its value is x", x, ic.instanceCreate ());
566
    }
566
    }
567
568
    
569
    public void testWeAreAbleToResetSharedClassObjectByCallingResetOnItIssue20962 () throws Exception {
570
        FileObject lookupFO;
571
        {
572
            Object x = Setting.findObject (Setting.class, true);
573
            lookupFO = lfs.findResource("/system/Services/lookupTest");
574
            DataFolder folderTest = DataFolder.findFolder(lookupFO);
575
            InstanceDataObject ido = InstanceDataObject.create (folderTest, "testLookupRefresh", x, null);
576
            lookupFO = ido.getPrimaryFile ();
577
            WeakReference ref = new WeakReference (ido);
578
            Setting.resetCalled = 0;
579
        }
580
581
        InstanceDataObject ido = (InstanceDataObject)DataObject.find (lookupFO);
582
        InstanceCookie ic = (InstanceCookie)ido.getCookie (InstanceCookie.class);
583
        assertNotNull ("Has cookie", ic);
584
        Object obj = ic.instanceCreate ();
585
        assertNotNull ("Not null", obj);
586
        assertEquals ("It is settings", Setting.class, obj.getClass ());
587
        
588
        
589
        FileLock lock = lookupFO.lock ();
590
        OutputStream os = lookupFO.getOutputStream (lock);
591
        
592
        PrintWriter pw = new PrintWriter (os);
593
        pw.println ("<?xml version=\"1.0\"?>");
594
        pw.println ("<!DOCTYPE settings PUBLIC \"-//NetBeans//DTD Session settings 1.0//EN\" \"http://www.netbeans.org/dtds/sessionsettings-1_0.dtd\">");
595
        pw.println ("<settings version=\"1.0\">");
596
        pw.println ("  <module name=\"org.openide/1\" spec=\"1.13\"/>");
597
        pw.println ("  <instanceof class=\"org.openide.options.SystemOption\"/>");
598
        pw.println ("  <instance class=\"" + Setting.class.getName () + "\"/>");
599
        pw.println ("</settings>");
600
        pw.close ();
601
        lock.releaseLock ();
602
        
603
        ic = (InstanceCookie)ido.getCookie (InstanceCookie.class);
604
        assertNotNull ("Has cookie", ic);
605
        assertNotNull ("Not null", obj);
606
        assertEquals ("It is settings", Setting.class, obj.getClass ());
607
        Setting s = (Setting)Setting.findObject (Setting.class, true);
608
        assertEquals ("Refresh has been called", 1, s.resetCalled);
609
    }
567
    
610
    
568
    /** Checks whether the instance is not saved multiple times.
611
    /** Checks whether the instance is not saved multiple times.
569
     *
612
     *
Lines 920-923 Link Here
920
        }
963
        }
921
    }
964
    }
922
965
966
    public static final class Setting extends org.openide.options.SystemOption {
967
        private static int resetCalled;
968
        
969
        protected void reset () {
970
            resetCalled++;
971
        }
972
        
973
        public String displayName () {
974
            return "My Setting";
975
        }
976
    }
923
}
977
}
(-)openide/test/unit/src/org/openide/options/SystemOptionTest.java (+69 lines)
Lines 93-98 Link Here
93
        }
93
        }
94
    }
94
    }
95
    
95
    
96
    //
97
    // Implements reset to default values
98
    //
99
    
100
    public void testSimpleResetToOldValuesWhenTheyWereInitializedInInitialize () throws Exception {
101
        SimpleOption s = (SimpleOption)SimpleOption.findObject (SimpleOption.class, true);
102
        
103
        s.setX (-10);
104
        s.setY ("-10");
105
        
106
        s.reset ();
107
        
108
        assertEquals ("Was 3 in initialize", 3, s.getX ());
109
        assertEquals ("Was hello", "hello", s.getY ());
110
    }
111
    
112
    public void testSimpleResetEvenWhenWeHaveStaticInitialValues () throws Exception {
113
        SimpleOption2 s = (SimpleOption2)SimpleOption.findObject (SimpleOption2.class, true);
114
        
115
        class PL implements java.beans.PropertyChangeListener {
116
            public int cnt;
117
            public String name;
118
            
119
            public void propertyChange (java.beans.PropertyChangeEvent ev) {
120
                cnt++;
121
                name = ev.getPropertyName ();
122
            }
123
            
124
            public void assertChange (String name, int cnt) {
125
                if (name != null) {
126
                    assertEquals ("The this property had to change", name, this.name);
127
                    this.name = null;
128
                }
129
                if (cnt != -1) {
130
                    assertEquals ("This number of times", cnt, this.cnt);
131
                    this.cnt = 0;
132
                }
133
            }
134
        }
135
        
136
        PL pl = new PL ();
137
        s.addPropertyChangeListener (pl);
138
        
139
        s.setX (-10);
140
        pl.assertChange ("x", 1);
141
        s.setX (-9);
142
        pl.assertChange ("x", 1);
143
        s.setY ("-10");
144
        pl.assertChange ("y", 1);
145
        s.setX2 (7777);
146
        s.setY2 ("-4444");
147
        
148
        s.reset ();
149
        
150
        assertEquals ("Was 3 in initialize", 3, s.getX ());
151
        assertEquals ("Was hello", "hello", s.getY ());
152
        assertEquals ("2 Was 3 in initialize", 3, s.getX2 ());
153
        assertEquals ("2 Was hello", "hello", s.getY2 ());
154
        
155
        
156
    }
157
    
96
    // XXX test that serialization works and matches deserialization
158
    // XXX test that serialization works and matches deserialization
97
    // (hint: use MaskingURLClassLoader from SharedClassObjectTest)
159
    // (hint: use MaskingURLClassLoader from SharedClassObjectTest)
98
    
160
    
Lines 133-138 Link Here
133
        public void setY(String y) {
195
        public void setY(String y) {
134
            putProperty("y", y, true);
196
            putProperty("y", y, true);
135
        }
197
        }
198
        
199
        public void reset () {
200
            super.reset ();
201
        }
136
    }
202
    }
137
    
203
    
138
    public static final class SimpleOption2 extends SystemOption {
204
    public static final class SimpleOption2 extends SystemOption {
Lines 174-179 Link Here
174
            String old = y2;
240
            String old = y2;
175
            y2 = nue;
241
            y2 = nue;
176
            firePropertyChange("y2", old, nue);
242
            firePropertyChange("y2", old, nue);
243
        }
244
        public void reset () {
245
            super.reset ();
177
        }
246
        }
178
    }
247
    }
179
    
248
    

Return to bug 20962