001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.sequence;
020
021import icy.common.CollapsibleEvent;
022import icy.util.StringUtil;
023
024public class SequenceEvent implements CollapsibleEvent
025{
026    public enum SequenceEventSourceType
027    {
028        SEQUENCE_TYPE, SEQUENCE_META, SEQUENCE_COLORMAP, SEQUENCE_COMPONENTBOUNDS, SEQUENCE_DATA, SEQUENCE_ROI, /**
029         * @deprecated
030         **/
031        @Deprecated
032        SEQUENCE_PAINTER, SEQUENCE_OVERLAY
033    }
034
035    public enum SequenceEventType
036    {
037        CHANGED, ADDED, REMOVED
038    }
039
040    private final Sequence sequence;
041    private final SequenceEventSourceType sourceType;
042    private SequenceEventType type;
043    private Object source;
044    private int param;
045
046    public SequenceEvent(Sequence sequence, SequenceEventSourceType sourceType)
047    {
048        this(sequence, sourceType, null, SequenceEventType.CHANGED, -1);
049    }
050
051    public SequenceEvent(Sequence sequence, SequenceEventSourceType sourceType, Object source)
052    {
053        this(sequence, sourceType, source, SequenceEventType.CHANGED, -1);
054    }
055
056    public SequenceEvent(Sequence sequence, SequenceEventSourceType sourceType, Object source, int param)
057    {
058        this(sequence, sourceType, source, SequenceEventType.CHANGED, param);
059    }
060
061    public SequenceEvent(Sequence sequence, SequenceEventSourceType sourceType, SequenceEventType type)
062    {
063        this(sequence, sourceType, null, type, -1);
064    }
065
066    public SequenceEvent(Sequence sequence, SequenceEventSourceType sourceType, Object source, SequenceEventType type)
067    {
068        this(sequence, sourceType, source, type, -1);
069    }
070
071    public SequenceEvent(Sequence sequence, SequenceEventSourceType sourceType, Object source, SequenceEventType type,
072            int param)
073    {
074        super();
075
076        this.sequence = sequence;
077        this.sourceType = sourceType;
078        this.source = source;
079        this.type = type;
080        this.param = param;
081    }
082
083    /**
084     * @return the sequence
085     */
086    public Sequence getSequence()
087    {
088        return sequence;
089    }
090
091    /**
092     * SourceType define the object type of <code>source</code><br>
093     * <br>
094     * The following source types are available:<br>
095     * <code>SEQUENCE_TYPE</code> --> source object is null<br>
096     * <code>SEQUENCE_META</code> --> source object define the meta data id (String)<br>
097     * It can be <i>null</i> (consider global metadata change)<br>
098     * <code>SEQUENCE_COLORMAP</code> --> source object is an instance of IcyColorModel<br>
099     * <code>SEQUENCE_COMPONENTBOUNDS</code> --> source object is an instance of IcyColorModel<br>
100     * <code>SEQUENCE_DATA</code> --> source object is an instance of IcyBufferedImage<br>
101     * source object can be null when severals images has been modified<br>
102     * <code>SEQUENCE_ROI</code> --> source object is an instance of ROI<br>
103     * source object can be null when severals images has been modified<br>
104     * <code>SEQUENCE_OVERLAY</code> --> source object is an instance of Overlay<br>
105     * source object can be null when severals images has been modified<br>
106     * <code>SEQUENCE_PAINTER</code> --> source object is an instance of Painter<br>
107     * source object can be null when severals images has been modified<br>
108     * <br>
109     */
110    public SequenceEventSourceType getSourceType()
111    {
112        return sourceType;
113    }
114
115    /**
116     * Source object of the event.<br>
117     * The object type here depend of the <code>sourceType</code> value.<br>
118     */
119    public Object getSource()
120    {
121        return source;
122    }
123
124    /**
125     * Type define the type of event.<br>
126     * <br>
127     * When <code>sourceType</code> is one of the following :<br>
128     * <code>SEQUENCE_TYPE, SEQUENCE_META, SEQUENCE_COLORMAP, SEQUENCE_COMPONENTBOUNDS</code><br>
129     * the type can only be <code>SequenceEventType.CHANGED</code><br>
130     * <br>
131     * When <code>sourceType</code> is one of the following :<br>
132     * <code>SEQUENCE_DATA, SEQUENCE_ROI, SEQUENCE_PAINTER, SEQUENCE_OVERLAY</code><br>
133     * the type can also be <code>SequenceEventType.ADDED</code> or <code>SequenceEventType.REMOVED</code><br>
134     * That mean a specific image, roi or painter (if <code>source != null</code>) has been added or
135     * removed from the sequence.<br>
136     * If <code>source == null</code> that mean we have a global change event and some stuff need to
137     * be recalculated.<br>
138     * Severals ADDED / CHANGED / REMOVE events can be compacted to one CHANGED event with a null
139     * source (global change) for SEQUENCE_DATA source type.
140     */
141    public SequenceEventType getType()
142    {
143        return type;
144    }
145
146    /**
147     * Extra parameter of event.<br>
148     * <br>
149     * It's used to specify the component number when <code>sourceType</code> is <code>SEQUENCE_COLORMAP</code> or
150     * <code>SEQUENCE_COMPONENTBOUNDS</code> (in both case source
151     * is instance of <code>IcyColorModel</code>).<br>
152     * Also used internally...
153     */
154    public int getParam()
155    {
156        return param;
157    }
158
159    @Override
160    public boolean collapse(CollapsibleEvent event)
161    {
162        if (equals(event))
163        {
164            final SequenceEvent e = (SequenceEvent) event;
165
166            switch (sourceType)
167            {
168                case SEQUENCE_COLORMAP:
169                case SEQUENCE_COMPONENTBOUNDS:
170                    // join events in one global event
171                    if (e.getParam() != param)
172                        param = -1;
173                    break;
174
175                case SEQUENCE_DATA:
176                    // optimize different type event to a single CHANGED event (for DATA only)
177                    if (e.getType() != type)
178                        type = SequenceEventType.CHANGED;
179                    if (e.getSource() != source)
180                        source = null;
181                    break;
182
183                default:
184                    break;
185            }
186
187            return true;
188        }
189
190        return false;
191    }
192
193    @Override
194    public int hashCode()
195    {
196        int res = sequence.hashCode() ^ sourceType.hashCode();
197
198        switch (sourceType)
199        {
200            case SEQUENCE_META:
201                if (source != null)
202                    res ^= source.hashCode();
203                break;
204
205            case SEQUENCE_PAINTER:
206            case SEQUENCE_OVERLAY:
207            case SEQUENCE_ROI:
208                res ^= type.hashCode();
209                if (source != null)
210                    res ^= source.hashCode();
211                break;
212
213            default:
214                break;
215        }
216
217        return res;
218    }
219
220    @Override
221    public boolean equals(Object obj)
222    {
223        if (obj instanceof SequenceEvent)
224        {
225            final SequenceEvent e = (SequenceEvent) obj;
226
227            // same source type
228            if ((e.getSequence() == sequence) && (e.getSourceType() == sourceType))
229            {
230                switch (sourceType)
231                {
232                    case SEQUENCE_META:
233                        return StringUtil.equals((String) e.getSource(), (String) source);
234
235                    case SEQUENCE_COLORMAP:
236                    case SEQUENCE_COMPONENTBOUNDS:
237                        return true;
238
239                    case SEQUENCE_DATA:
240                        return true;
241
242                    case SEQUENCE_PAINTER:
243                    case SEQUENCE_OVERLAY:
244                    case SEQUENCE_ROI:
245                        return ((e.getType() == type) && (e.getSource() == source));
246
247                    case SEQUENCE_TYPE:
248                        return true;
249                }
250            }
251        }
252
253        return super.equals(obj);
254    }
255}