Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.event;
027
028// PENDING_DOC_REVIEW
029
030import java.io.InvalidObjectException;
031import java.io.ObjectStreamException;
032import java.io.Serializable;
033import java.util.ArrayList;
034import java.util.Deque;
035import java.util.Iterator;
036import java.util.LinkedList;
037import java.util.List;
038import java.util.Set;
039import java.util.WeakHashMap;
040
041/**
042 * This class represents a specific event type associated with an {@code Event}.
043 * <p>
044 * Event types form a hierarchy with the {@link EventType#ROOT} (equals to
045 * {@link Event#ANY}) as its root. This is useful in event filter / handler
046 * registration where a single event filter / handler can be registered to a
047 * super event type and will be receiving its sub type events as well.
048 * Note that you cannot construct two different EventType objects with the same
049 * name and parent.
050 *
051 * <p>
052 * <b>Note about deserialization</b>: All EventTypes that are going to be deserialized
053 * (e.g. as part of {@link Event} deserialization), need to exist at the time of
054 * deserialization. Deserialization of EventType will not create new EventType
055 * objects.
056 * 
057 * @param <T> the event class to which this type applies
058 */
059public final class EventType<T extends Event> implements Serializable{
060    
061    /**
062     * The root event type. All other event types are either direct or
063     * indirect sub types of it. It is also the only event type which
064     * has its super event type set to {@code null}.
065     */
066    public static final EventType<Event> ROOT = 
067            new EventType<Event>("EVENT", null);
068    
069    private WeakHashMap<EventType<? extends T>, Void> subTypes;
070    
071    private final EventType<? super T> superType;
072
073    private final String name;
074
075    /**
076     * Constructs a new {@code EventType} with the {@code EventType.ROOT} as its
077     * super type and the name set to {@code null}.
078     * @deprecated Do not use this constructor, as only one such EventType can exist
079     */
080    @Deprecated
081    public EventType() {
082        this(ROOT, null);
083    }
084
085    /**
086     * Constructs a new {@code EventType} with the specified name and the
087     * {@code EventType.ROOT} as its super type.
088     *
089     * @param name the name
090     * @throws IllegalArgumentException if an EventType with the same name and 
091     * {@link EventType#ROOT}/{@link Event#ANY} as parent
092     */
093    public EventType(final String name) {
094        this(ROOT, name);
095    }
096
097    /**
098     * Constructs a new {@code EventType} with the specified super type and
099     * the name set to {@code null}.
100     *
101     * @param superType the event super type
102     * @throws IllegalArgumentException if an EventType with "null" name and 
103     * under this supertype exists
104     */
105    public EventType(final EventType<? super T> superType) {
106        this(superType, null);
107    }
108
109    /**
110     * Constructs a new {@code EventType} with the specified super type and
111     * name.
112     *
113     * @param superType the event super type
114     * @param name the name
115     * @throws IllegalArgumentException if an EventType with the same name and 
116     * superType exists
117     */
118    public EventType(final EventType<? super T> superType,
119            final String name) {
120        if (superType == null) {
121            throw new NullPointerException(
122                    "Event super type must not be null!");
123        }
124
125        this.superType = superType;
126        this.name = name;
127        superType.register(this);
128    }
129    
130    /**
131     * Internal constructor that skips various checks
132     */
133    EventType(final String name,
134                      final EventType<? super T> superType) {
135        this.superType = superType;
136        this.name = name;
137        if (superType != null) {
138            if (superType.subTypes != null) {
139                for (Iterator i = superType.subTypes.keySet().iterator(); i.hasNext();) {
140                    EventType t  = (EventType) i.next();
141                    if (name == null && t.name == null || (name != null && name.equals(t.name))) {
142                        i.remove();
143                    }
144                }
145            }
146            superType.register(this);
147        }
148    }
149
150    /**
151     * Gets the super type of this event type. The returned value is
152     * {@code null} only for the {@code EventType.ROOT}.
153     *
154     * @return the super type
155     */
156    public final EventType<? super T> getSuperType() {
157        return superType;
158    }
159
160    /**
161     * Gets the name of this event type.
162     *
163     * @return the name
164     */
165    public final String getName() {
166        return name;
167    }
168
169    /**
170     * Returns a string representation of this {@code EventType} object.
171     * @return a string representation of this {@code EventType} object.
172     */ 
173    @Override
174    public String toString() {
175        return (name != null) ? name : super.toString();
176    }
177    
178    private void register(javafx.event.EventType<? extends T> subType) {
179        if (subTypes == null) {
180            subTypes = new WeakHashMap<EventType<? extends T>, Void>();
181        }
182        for (EventType<? extends T> t : subTypes.keySet()) {
183            if (((t.name == null && subType.name == null) || (t.name != null && t.name.equals(subType.name)))) {
184                throw new IllegalArgumentException("EventType \"" + subType + "\""
185                        + "with parent \"" + subType.getSuperType()+"\" already exists");
186            }
187        }
188        subTypes.put(subType, null);
189    }
190    
191    private Object writeReplace() throws ObjectStreamException {
192        Deque<String> path = new LinkedList<String>();
193        EventType<?> t = this;
194        while (t != ROOT) {
195            path.addFirst(t.name);
196            t = t.superType;
197        }
198        return new EventTypeSerialization(new ArrayList<String>(path));
199    }
200    
201    static class EventTypeSerialization implements Serializable {
202        private List<String> path;
203
204        public EventTypeSerialization(List<String> path) {
205            this.path = path;
206        }
207
208        private Object readResolve() throws ObjectStreamException {
209            EventType t = ROOT;
210            for (int i = 0; i < path.size(); ++i) {
211                String p = path.get(i);
212                if (t.subTypes != null) {
213                    EventType s = findSubType(t.subTypes.keySet(), p);
214                    if (s == null) {
215                        throw new InvalidObjectException("Cannot find event type \"" + p + "\" (of " + t + ")");
216                    }
217                    t = s;
218                } else {
219                    throw new InvalidObjectException("Cannot find event type \"" + p + "\" (of " + t + ")");
220                }
221            }
222            return t;
223        }
224        
225        private EventType findSubType(Set<EventType> subTypes, String name) {
226            for (EventType t : subTypes) {
227                if (((t.name == null && name == null) || (t != null && t.name.equals(name)))) {
228                    return t;
229                }
230            }
231            return null;
232        }
233
234    }
235}