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

Last change on this file since 849 was 837, checked in by sherbold, 12 years ago
  • code documentation and clean-up
File size: 10.8 KB
RevLine 
[837]1
[619]2package de.ugoe.cs.quest.plugin.mfc.guimodel;
3
[1]4import java.util.ArrayList;
5import java.util.List;
6
[619]7import de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec;
[1]8import de.ugoe.cs.util.StringTools;
9
10/**
11 * <p>
[619]12 * This class implements a node in the {@link WindowTree} that is maintained during parsing a
13 * session.
[1]14 * </p>
15 * <p>
[619]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.
[1]18 * </p>
19 *
20 * @author Steffen Herbold
[171]21 * @version 1.0
[1]22 */
[619]23public class MFCGUIElementSpec implements IGUIElementSpec {
[1]24
[619]25    /**
26     * <p>
[778]27     * Id for object serialization.
28     * </p>
29     */
30    private static final long serialVersionUID = 1L;
31
32    /**
33     * <p>
[619]34     * current name of the window
35     * </p>
36     */
37    private String name;
[1]38
[619]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>();
[1]45
[619]46    /**
47     * <p>
48     * Handle of the window. Used as unique identifier during its existence.
49     * </p>
50     */
51    private long hwnd;
[1]52
[619]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>();
[1]59
[619]60    /**
61     * <p>
62     * Resource id of the window.
63     * </p>
64     */
65    private final int resourceId;
[1]66
[619]67    /**
68     * <p>
69     * type (class name) of the window.
70     * </p>
71     */
72    private final String type;
[1]73
[619]74    /**
75     * <p>
76     * True, if the window is modal.
77     * </p>
78     */
79    private final boolean isModal;
[171]80
[619]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     */
[837]102    protected MFCGUIElementSpec(long hwnd, String name, int resourceId, String type, boolean isModal)
[619]103    {
104        this.hwnd = hwnd;
105        this.name = name;
106        this.resourceId = resourceId;
107        this.type = type;
108        this.isModal = isModal;
109    }
[1]110
[619]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();
[837]120
[619]121        if (name != null) {
122            names.append('"');
123            names.append(name);
124            names.append('"');
125        }
126        else {
127            names.append("NOT_SET");
128        }
[837]129
[619]130        if (formerNames.size() > 0) {
[837]131
[619]132            names.append(" (aka ");
[837]133
[619]134            for (int i = 0; i < formerNames.size(); i++) {
135                if (i > 0) {
136                    names.append("/");
137                }
[1]138
[619]139                names.append('"');
140                names.append(formerNames.get(i));
141                names.append('"');
142            }
[837]143
[619]144            names.append(")");
145        }
[837]146
[619]147        return names.toString();
148    }
[1]149
[619]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    }
[1]160
[619]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    }
[1]171
[837]172    /*
173     * (non-Javadoc)
174     *
[619]175     * @see de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#getType()
176     */
177    @Override
178    public String getType() {
179        return type;
180    }
[1]181
[619]182    /**
183     * <p>
[837]184     * Returns the modality of the specified GUI element.
[619]185     * </p>
[837]186     *
187     * @return the modality
[619]188     */
189    public boolean isModal() {
190        return isModal;
191    }
[1]192
[619]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) {
[837]202        if ((this.name != null) && (!this.name.equals(newName)) &&
[619]203            (!this.formerNames.contains(this.name)))
204        {
205            this.formerNames.add(this.name);
206        }
[837]207
[619]208        this.name = newName;
209    }
[1]210
[619]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        }
[837]223
[619]224        this.hwnd = newHwnd;
225    }
[1]226
[837]227    /*
228     * (non-Javadoc)
229     *
230     * @see
231     * de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#getSimilarity(de.ugoe.cs.quest.eventcore
232     * .guimodel.IGUIElementSpec)
[619]233     */
234    @Override
235    public boolean getSimilarity(IGUIElementSpec other) {
[837]236
[619]237        if (this == other) {
238            return true;
239        }
[837]240
[619]241        if (!(other instanceof MFCGUIElementSpec)) {
242            return false;
243        }
[837]244
[619]245        MFCGUIElementSpec otherSpec = (MFCGUIElementSpec) other;
[1]246
[619]247        if ((type != otherSpec.type) && ((type != null) && (!type.equals(otherSpec.type)))) {
248            return false;
249        }
[1]250
[619]251        if (isModal != otherSpec.isModal) {
252            return false;
253        }
[1]254
[619]255        if (resourceId != otherSpec.resourceId) {
256            return false;
257        }
[171]258
[619]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
[837]264
[619]265        if (hwnd == otherSpec.hwnd) {
266            return true;
267        }
[837]268
[619]269        if ((name != null) && (name.equals(otherSpec.name))) {
270            return true;
271        }
[837]272
273        if ((((name == null) && (otherSpec.name == null)) || (("".equals(name)) && (""
274            .equals(otherSpec.name)))) &&
275            (formerNames.size() == 0) &&
276            (otherSpec.formerNames.size() == 0))
[619]277        {
278            return true;
279        }
[837]280
[619]281        // if the hwnd and the name did not stay the same, then the name should be checked first.
[715]282        // The current name of one of the specs must be contained in the former names of the
[619]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.
[1]288
[715]289        if ((otherSpec.name != null) && formerNames.contains(otherSpec.name)) {
[619]290            return true;
291        }
292
293        if ((name != null) && otherSpec.formerNames.contains(name)) {
294            return true;
295        }
[837]296
[715]297        // ok. Even the names do not match. This is usually a clear indication, that the elements
[619]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
[837]307
[619]308        return false;
309    }
310
[837]311    /*
312     * (non-Javadoc)
313     *
[619]314     * @see de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#equals(IGUIElementSpec)
315     */
316    @Override
[832]317    public boolean equals(Object other) {
[837]318
[619]319        if (this == other) {
320            return true;
321        }
[837]322
[619]323        if (!(other instanceof MFCGUIElementSpec)) {
324            return false;
325        }
[837]326
[619]327        MFCGUIElementSpec otherSpec = (MFCGUIElementSpec) other;
[837]328
329        return (hwnd == otherSpec.hwnd) && (isModal == otherSpec.isModal) &&
[619]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
[837]335    /*
336     * (non-Javadoc)
337     *
[619]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>
[837]348     * Returns a string identifier of the window:<br>
[619]349     * {@code [resourceId;"windowName";"className";modality]}
350     * </p>
351     *
352     * @return identifier string of the window
353     */
354    @Override
355    public String toString() {
[837]356        return "[" + resourceId + ";" + getName() + ";\"" + type + "\";" + isModal + ";" + hwnd +
357            "]";
[619]358    }
359
360    /**
361     * <p>
[837]362     * Returns the XML representation of this specification.
[619]363     * </p>
[837]364     *
365     * @return the XML representation
[619]366     */
367    String toXML() {
[837]368        return "<window name=\"" + (name != null ? StringTools.xmlEntityReplacement(name) : "") +
369            "\" class=\"" + StringTools.xmlEntityReplacement(type) + "\" resourceId=\"" +
370            resourceId + "\" isModal=\"" + isModal + "\"/>";
[619]371    }
372
373    /**
374     * <p>
[837]375     * Updates the specification with another specification.
[619]376     * </p>
[837]377     *
[619]378     * @param furtherSpec
[837]379     *            specification used to update the current specification
[619]380     */
381    void update(IGUIElementSpec furtherSpec) {
382        MFCGUIElementSpec other = (MFCGUIElementSpec) furtherSpec;
[837]383
[619]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
[837]397            if ((name != other.name) && (name != null) && (!name.equals(other.name))) {
[619]398                setName(other.name);
399            }
400        }
401    }
402
[1]403}
Note: See TracBrowser for help on using the repository browser.