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

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