1/*
2 * Copyright 2007 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.mockftpserver.core.command;
17
18import java.net.InetAddress;
19import java.util.Collections;
20import java.util.Date;
21import java.util.HashMap;
22import java.util.Map;
23import java.util.Set;
24
25import org.mockftpserver.core.util.Assert;
26import org.mockftpserver.core.util.AssertFailedException;
27
28/**
29 * Represents information about a single FTP Command invocation. Manages and provides access to
30 * the Command, the host address (<code>InetAddress</code>) of the client that submitted the
31 * Command and the timestamp of the Command submission.
32 * <p>
33 * This class also supports storing zero or more arbitrary mappings of <i>key</i> to value, where <i>key</i> is
34 * a String and <i>value</i> is any Object. Convenience methods are provided that enable retrieving
35 * type-specific data by its <i>key</i>. The data stored in an {@link InvocationRecord} is CommandHandler-specific.
36 * <p>
37 * The {@link #lock()} method makes an instance of this class immutable. After an instance is locked,
38 * calling the {@link #set(String, Object)} method will throw an <code>AssertFailedException</code>.
39 *
40 * @version $Revision$ - $Date$
41 *
42 * @author Chris Mair
43 */
44public class InvocationRecord {
45
46    private Command command;
47    private Date time;
48    private InetAddress clientHost;
49    private Map data = new HashMap();
50    private boolean locked = false;
51
52    /**
53     * Create a new instance
54     * @param command - the Command
55     * @param clientHost - the client host
56     */
57    public InvocationRecord(Command command, InetAddress clientHost) {
58        this.command = command;
59        this.time = new Date();
60        this.clientHost = clientHost;
61    }
62
63    /**
64     * Lock this instance, making it immutable. After an instance is locked,
65     * calling the {@link #set(String, Object)} method will throw an
66     * <code>AssertFailedException</code>.
67     */
68    public void lock() {
69        locked = true;
70    }
71
72    /**
73     * Return true if this object has been locked, false otherwise. See {@link #lock()}.
74     * @return true if this object has been locked, false otherwise.
75     */
76    public boolean isLocked() {
77        return locked;
78    }
79
80    /**
81     * @return the client host that submitted the command, as an InetAddress
82     */
83    public InetAddress getClientHost() {
84        return clientHost;
85    }
86
87    /**
88     * @return the Command
89     */
90    public Command getCommand() {
91        return command;
92    }
93
94    /**
95     * @return the time that the command was processed; this may differ slightly from when the command was received.
96     */
97    public Date getTime() {
98        // Return a copy of the Date object to preserve immutability
99        return new Date(time.getTime());
100    }
101
102    /**
103     * Store the value for the specified key. If this object already contained a mapping
104     * for this key, the old value is replaced by the specified value. This method throws
105     * an <code>AssertFailedException</code> if this object has been locked. See {@link #lock()}.
106     *
107     * @param key - the key; must not be null
108     * @param value - the value to store for the specified key
109     *
110     * @throws AssertFailedException - if the key is null or this object has been locked.
111     */
112    public void set(String key, Object value) {
113        Assert.notNull(key, "key");
114        Assert.isFalse(locked, "The InvocationRecord is locked!");
115        data.put(key, value);
116    }
117
118    /**
119     * Returns <code>true</code> if this object contains a mapping for the specified key.
120     *
121     * @param key - the key; must not be null
122     * @return <code>true</code> if there is a mapping for the key
123     *
124     * @throws AssertFailedException - if the key is null
125     */
126    public boolean containsKey(String key) {
127        Assert.notNull(key, "key");
128        return data.containsKey(key);
129    }
130
131    /**
132     * Returns a Set view of the keys for the data stored in this object.
133     * Changes to the returned Set have no effect on the data stored within this object
134     * .
135     * @return the Set of keys for the data stored within this object
136     */
137    public Set keySet() {
138        return Collections.unmodifiableSet(data.keySet());
139    }
140
141    /**
142     * Get the String value associated with the specified key. Returns null if there is
143     * no mapping for this key. A return value of null does not necessarily indicate that
144     * this object contains no mapping for the key; it's also possible that the value was
145     * explicitly set to null for the key. The containsKey operation may be used to
146     * distinguish these two cases.
147     *
148     * @param key - the key; must not be null
149     * @return the String data stored at the specified key; may be null
150     *
151     * @throws ClassCastException - if the object for the specified key is not a String
152     * @throws AssertFailedException - if the key is null
153     */
154    public String getString(String key) {
155        Assert.notNull(key, "key");
156        return (String) data.get(key);
157    }
158
159    /**
160     * Get the Object value associated with the specified key. Returns null if there is
161     * no mapping for this key. A return value of null does not necessarily indicate that
162     * this object contains no mapping for the key; it's also possible that the value was
163     * explicitly set to null for the key. The containsKey operation may be used to
164     * distinguish these two cases.
165     *
166     * @param key - the key; must not be null
167     * @return the data stored at the specified key, as an Object; may be null
168     *
169     * @throws AssertFailedException - if the key is null
170     */
171    public Object getObject(String key) {
172        Assert.notNull(key, "key");
173        return data.get(key);
174    }
175
176    /**
177     * Return the String representation of this object
178     * @see java.lang.Object#toString()
179     */
180    public String toString() {
181        return "InvocationRecord[time=" + time + " client-host=" + clientHost + " command=" + command + " data="+ data + "]";
182    }
183}
184