001 /*
002 Copyright (c) 1996-2012, Damon Hart-Davis
003 All rights reserved.
004
005 Redistribution and use in source and binary forms, with or without
006 modification, are permitted provided that the following conditions are
007 met:
008
009 * Redistributions of source code must retain the above copyright
010 notice, this list of conditions and the following disclaimer.
011
012 * Redistributions in binary form must reproduce the above copyright
013 notice, this list of conditions and the following disclaimer in the
014 documentation and/or other materials provided with the
015 distribution.
016
017 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
018 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
019 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
020 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
021 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
022 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
024 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
025 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
027 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028 */
029
030 package org.hd.d.pg2k.clApp.uploader;
031
032 import java.io.ByteArrayOutputStream;
033 import java.io.File;
034 import java.io.IOException;
035 import java.io.InputStream;
036 import java.util.Properties;
037
038 import org.hd.d.pg2k.svrCore.ExhibitName;
039
040
041 /**Holder for uploader mutable persistent properties, preferences, etc.
042 * Holds a small amount of structured information
043 * that can be persisted from one uploader run to the next.
044 * <p>
045 * Is thread-safe, though using multiple threads at once,
046 * especially mutators, may be confusing.
047 * <p>
048 * Not designed to be Serialisable,
049 * but instead can be stored in the plain-text Java properties format.
050 * <p>
051 * Is designed to be GUI-free.
052 * <p>
053 * Package visible since need be seen only by the main GUI class.
054 * <p>
055 * TODO: ADD locale
056 * <p>
057 * TODO: ADD upload category / main words / number-in-series / type / description
058 */
059 final class UploaderProps
060 {
061 /**Maximum length (in bytes) expected necessary to persist this data; strictly positive. */
062 public static final int MAX_PERS_BYTES = 8192;
063
064
065 /**Internal flag to note if any flags have changed.
066 * Volatile for thread-safe access without a lock.
067 * <p>
068 * Because of possible races this cannot be guaranteed to catch all changes,
069 * so a final "save-on-exit" and/or periodic save would be valuable.
070 */
071 private volatile boolean changed;
072
073
074 /**User ID (author initials), initially null; null or valid author initials.
075 * Would usually be the last set of initials (ie user ID) used.
076 * <p>
077 * Volatile for thread-safe access without a lock.
078 */
079 private volatile String userID;
080
081 /**Get the user ID (author initials), initially null; null or valid author initials.
082 * Usually be the last set of initials (ie user ID) used
083 * to attempt a server connection.
084 */
085 public String getUserID() { return(userID); }
086
087 /**Set the user ID (author initials), null or valid author initials.
088 * An attempt to set with an invalid value is treated as if null.
089 */
090 public void setUserID(final String authInitials)
091 {
092 if(!ExhibitName.validAuthorSyntax(authInitials))
093 {
094 if(userID == null) { return; } // Nothing to do...
095 changed = true;
096 userID = null;
097 }
098 else
099 {
100 if(authInitials.equals(userID)) { return; } // Nothing to do...
101 changed = true;
102 userID = authInitials;
103 }
104 }
105
106
107
108 /**File chooser directory hint, or null.
109 * The last directory that a user looked in for files, eg to upload.
110 * <p>
111 * Volatile for thread-safe access without a lock.
112 */
113 private volatile File fileChooserPathHint;
114
115 /**Get the file chooser directory hint, or null.
116 * The last directory that a user looked in for files, eg to upload.
117 */
118 public File getFileChooserPathHint() { return(fileChooserPathHint); }
119
120 /**Set the file chooser directory hint, or null.
121 */
122 public void setFileChooserPathHint(final File pathHint)
123 {
124 if(pathHint == null)
125 {
126 if(fileChooserPathHint == null) { return; } // Nothing to do...
127 changed = true;
128 fileChooserPathHint = null;
129 }
130 else
131 {
132 if(pathHint.equals(fileChooserPathHint)) { return; } // Nothing to do...
133 changed = true;
134 fileChooserPathHint = pathHint;
135 }
136 }
137
138
139
140
141 /**Get data in properties-file format byte[]; never null. */
142 public byte[] getPersistentData()
143 {
144 final Properties props = new Properties();
145
146 final String u = userID;
147 if(u != null) { props.put("userID", u); }
148 final File ph = fileChooserPathHint;
149 if(ph != null) { props.put("fileChooserPathHint", ph.getPath()); }
150
151 final ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
152 try { props.store(baos, null); }
153 catch(final IOException e) { throw new Error("INTERNAL ERROR"); }
154
155 return(baos.toByteArray());
156 }
157
158 /**Get data in properties file if properties have changes, else null.
159 * The changed flag is cleared if this routine returns a non-null value,
160 * thus it can be used to save incremental updates asynchronously.
161 */
162 public byte[] getPersistentDataIfChanged()
163 {
164 if(!changed) { return(null); }
165
166 // Clear changed flag *before* getting data to help reduce race effects.
167 changed = false;
168 return(getPersistentData());
169 }
170
171 /**Load from previously-generated persistent data format.
172 * Any invalid data is simply ignored.
173 * <p>
174 * All updates are done as if through the setXXX() members
175 * to validate/coerce them, concurrently with other use of this instance.
176 */
177 public void loadFromPersistentData(final InputStream is)
178 throws IOException
179 {
180 if(is == null) { throw new IllegalArgumentException(); }
181 final Properties props = new Properties();
182 props.load(is);
183
184 // Retrieve the values from the properties set.
185 setUserID(props.getProperty("userID"));
186 final String ph = props.getProperty("fileChooserPathHint");
187 setFileChooserPathHint((ph == null) ? null : new File(ph));
188 }
189 }