source: trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/guimodel/MFCGUIElementSpec.java @ 837

Last change on this file since 837 was 837, checked in by sherbold, 12 years ago
  • code documentation and clean-up
File size: 10.8 KB
Line 
1
2package de.ugoe.cs.quest.plugin.mfc.guimodel;
3
4import java.util.ArrayList;
5import java.util.List;
6
7import de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec;
8import de.ugoe.cs.util.StringTools;
9
10/**
11 * <p>
12 * This class implements a node in the {@link WindowTree} that is maintained during parsing a
13 * session.
14 * </p>
15 * <p>
16 * The window tree is structure that contains the hierarchy of the windows of a application as well
17 * as basic information about each window: the hwnd; its name; its resource id; its class name.
18 * </p>
19 *
20 * @author Steffen Herbold
21 * @version 1.0
22 */
23public class MFCGUIElementSpec implements IGUIElementSpec {
24
25    /**
26     * <p>
27     * Id for object serialization.
28     * </p>
29     */
30    private static final long serialVersionUID = 1L;
31
32    /**
33     * <p>
34     * current name of the window
35     * </p>
36     */
37    private String name;
38
39    /**
40     * <p>
41     * previous names of the window as it may have changed over time.
42     * </p>
43     */
44    private List<String> formerNames = new ArrayList<String>();
45
46    /**
47     * <p>
48     * Handle of the window. Used as unique identifier during its existence.
49     * </p>
50     */
51    private long hwnd;
52
53    /**
54     * <p>
55     * previous handles of the window as the window may have been destroyed and recreated
56     * </p>
57     */
58    private List<Long> formerHwnds = new ArrayList<Long>();
59
60    /**
61     * <p>
62     * Resource id of the window.
63     * </p>
64     */
65    private final int resourceId;
66
67    /**
68     * <p>
69     * type (class name) of the window.
70     * </p>
71     */
72    private final String type;
73
74    /**
75     * <p>
76     * True, if the window is modal.
77     * </p>
78     */
79    private final boolean isModal;
80
81    /**
82     * <p>
83     * Creates a new WindowTreeNode.
84     * </p>
85     * <p>
86     * The constructor is protected WindowTreeNode may only be created from the WindowTree.
87     * </p>
88     *
89     * @param hwnd
90     *            hwnd of the window
91     * @param parent
92     *            reference to the parent's WindowTreeNode
93     * @param name
94     *            name of the window
95     * @param resourceId
96     *            resource id of the window
97     * @param type
98     *            type, i.e. class name of the window
99     * @param isModal
100     *            modality of the window
101     */
102    protected MFCGUIElementSpec(long hwnd, String name, int resourceId, String type, boolean isModal)
103    {
104        this.hwnd = hwnd;
105        this.name = name;
106        this.resourceId = resourceId;
107        this.type = type;
108        this.isModal = isModal;
109    }
110
111    /**
112     * <p>
113     * Returns the name of the window.
114     * </p>
115     *
116     * @return name of the window
117     */
118    public String getName() {
119        StringBuffer names = new StringBuffer();
120
121        if (name != null) {
122            names.append('"');
123            names.append(name);
124            names.append('"');
125        }
126        else {
127            names.append("NOT_SET");
128        }
129
130        if (formerNames.size() > 0) {
131
132            names.append(" (aka ");
133
134            for (int i = 0; i < formerNames.size(); i++) {
135                if (i > 0) {
136                    names.append("/");
137                }
138
139                names.append('"');
140                names.append(formerNames.get(i));
141                names.append('"');
142            }
143
144            names.append(")");
145        }
146
147        return names.toString();
148    }
149
150    /**
151     * <p>
152     * Returns the hwnd of the window.
153     * </p>
154     *
155     * @return hwnd of the window
156     */
157    public long getHwnd() {
158        return hwnd;
159    }
160
161    /**
162     * <p>
163     * Returns the resource id of the window.
164     * </p>
165     *
166     * @return resource id of the window
167     */
168    public int getResourceId() {
169        return resourceId;
170    }
171
172    /*
173     * (non-Javadoc)
174     *
175     * @see de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#getType()
176     */
177    @Override
178    public String getType() {
179        return type;
180    }
181
182    /**
183     * <p>
184     * Returns the modality of the specified GUI element.
185     * </p>
186     *
187     * @return the modality
188     */
189    public boolean isModal() {
190        return isModal;
191    }
192
193    /**
194     * <p>
195     * Sets the name of the window.
196     * </p>
197     *
198     * @param text
199     *            new name of the window
200     */
201    public void setName(String newName) {
202        if ((this.name != null) && (!this.name.equals(newName)) &&
203            (!this.formerNames.contains(this.name)))
204        {
205            this.formerNames.add(this.name);
206        }
207
208        this.name = newName;
209    }
210
211    /**
212     * <p>
213     * Sets the hwnd of the window.
214     * </p>
215     *
216     * @param text
217     *            new name of the window
218     */
219    public void setHwnd(long newHwnd) {
220        if (!this.formerHwnds.contains(this.hwnd)) {
221            this.formerHwnds.add(this.hwnd);
222        }
223
224        this.hwnd = newHwnd;
225    }
226
227    /*
228     * (non-Javadoc)
229     *
230     * @see
231     * de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#getSimilarity(de.ugoe.cs.quest.eventcore
232     * .guimodel.IGUIElementSpec)
233     */
234    @Override
235    public boolean getSimilarity(IGUIElementSpec other) {
236
237        if (this == other) {
238            return true;
239        }
240
241        if (!(other instanceof MFCGUIElementSpec)) {
242            return false;
243        }
244
245        MFCGUIElementSpec otherSpec = (MFCGUIElementSpec) other;
246
247        if ((type != otherSpec.type) && ((type != null) && (!type.equals(otherSpec.type)))) {
248            return false;
249        }
250
251        if (isModal != otherSpec.isModal) {
252            return false;
253        }
254
255        if (resourceId != otherSpec.resourceId) {
256            return false;
257        }
258
259        // up to now, we compared, if the basics match. Now lets compare the id and the
260        // name. Both may change. The name may be reset (e.g. the title of a frame using the
261        // asterisk in the case data was changed). The id may change if e.g. a dialog is closed
262        // and reopend, i.e. a new instance is created. If one of them stays the same, then
263        // similarity is given. Therefore these are the first two comparisons
264
265        if (hwnd == otherSpec.hwnd) {
266            return true;
267        }
268
269        if ((name != null) && (name.equals(otherSpec.name))) {
270            return true;
271        }
272
273        if ((((name == null) && (otherSpec.name == null)) || (("".equals(name)) && (""
274            .equals(otherSpec.name)))) &&
275            (formerNames.size() == 0) &&
276            (otherSpec.formerNames.size() == 0))
277        {
278            return true;
279        }
280
281        // if the hwnd and the name did not stay the same, then the name should be checked first.
282        // The current name of one of the specs must be contained in the former names of the
283        // respective other spec for similarity. Either of the specs should contain the name of the
284        // respective other spec in its former names. We can rely on this, as in the MFC context
285        // we get to know each name change. I.e. although currently the names of the specs differ,
286        // once they were identical. But it is sufficient to do it for the current names of the
287        // elements, as only one of them may have experienced more name changes then the other.
288
289        if ((otherSpec.name != null) && formerNames.contains(otherSpec.name)) {
290            return true;
291        }
292
293        if ((name != null) && otherSpec.formerNames.contains(name)) {
294            return true;
295        }
296
297        // ok. Even the names do not match. This is usually a clear indication, that the elements
298        // are distinct. However, we check, if the former handles matched. This is very unlikely
299        // to happen. But it may occur, if a GUI element does not have a name or its name stays
300        // the empty string and if this GUI element is created, destroyed, and created again.
301
302        if (formerHwnds.contains(otherSpec.hwnd) || otherSpec.formerHwnds.contains(hwnd)) {
303            return true;
304        }
305
306        // now we can be really sure, that the GUI elements differ
307
308        return false;
309    }
310
311    /*
312     * (non-Javadoc)
313     *
314     * @see de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#equals(IGUIElementSpec)
315     */
316    @Override
317    public boolean equals(Object other) {
318
319        if (this == other) {
320            return true;
321        }
322
323        if (!(other instanceof MFCGUIElementSpec)) {
324            return false;
325        }
326
327        MFCGUIElementSpec otherSpec = (MFCGUIElementSpec) other;
328
329        return (hwnd == otherSpec.hwnd) && (isModal == otherSpec.isModal) &&
330            (resourceId == otherSpec.resourceId) &&
331            ((type == otherSpec.type) || ((type != null) && (type.equals(otherSpec.type)))) &&
332            ((name == otherSpec.name) || ((name != null) && (name.equals(otherSpec.name))));
333    }
334
335    /*
336     * (non-Javadoc)
337     *
338     * @see java.lang.Object#hashCode()
339     */
340    @Override
341    public int hashCode() {
342        // reuse only invariable elements
343        return (type + isModal + resourceId).hashCode();
344    }
345
346    /**
347     * <p>
348     * Returns a string identifier of the window:<br>
349     * {@code [resourceId;"windowName";"className";modality]}
350     * </p>
351     *
352     * @return identifier string of the window
353     */
354    @Override
355    public String toString() {
356        return "[" + resourceId + ";" + getName() + ";\"" + type + "\";" + isModal + ";" + hwnd +
357            "]";
358    }
359
360    /**
361     * <p>
362     * Returns the XML representation of this specification.
363     * </p>
364     *
365     * @return the XML representation
366     */
367    String toXML() {
368        return "<window name=\"" + (name != null ? StringTools.xmlEntityReplacement(name) : "") +
369            "\" class=\"" + StringTools.xmlEntityReplacement(type) + "\" resourceId=\"" +
370            resourceId + "\" isModal=\"" + isModal + "\"/>";
371    }
372
373    /**
374     * <p>
375     * Updates the specification with another specification.
376     * </p>
377     *
378     * @param furtherSpec
379     *            specification used to update the current specification
380     */
381    void update(IGUIElementSpec furtherSpec) {
382        MFCGUIElementSpec other = (MFCGUIElementSpec) furtherSpec;
383
384        if (other != this) {
385            for (long formerHwnd : other.formerHwnds) {
386                setHwnd(formerHwnd);
387            }
388
389            if (hwnd != other.hwnd) {
390                hwnd = other.hwnd;
391            }
392
393            for (String formerName : other.formerNames) {
394                setName(formerName);
395            }
396
397            if ((name != other.name) && (name != null) && (!name.equals(other.name))) {
398                setName(other.name);
399            }
400        }
401    }
402
403}
Note: See TracBrowser for help on using the repository browser.