1 package com.atlassian.sal.core.pluginsettings;
2
3 import java.io.*;
4 import java.util.*;
5 import java.util.Map.Entry;
6
7 import com.atlassian.sal.api.pluginsettings.PluginSettings;
8 import com.google.common.collect.Lists;
9 import com.google.common.base.Function;
10 import org.apache.commons.lang.Validate;
11 import org.apache.log4j.Logger;
12
13
14
15
16
17 public abstract class AbstractStringPluginSettings implements PluginSettings
18 {
19 private static final Logger log = Logger.getLogger(AbstractStringPluginSettings.class);
20
21 private static final String PROPERTIES_ENCODING = "ISO8859_1";
22 private static final String PROPERTIES_IDENTIFIER = "java.util.Properties";
23 private static final String LIST_IDENTIFIER = "#java.util.List";
24 private static final String MAP_IDENTIFIER = "#java.util.Map";
25
26
27
28
29
30
31
32
33 private final boolean isDeveloperMode = Boolean.getBoolean("atlassian.dev.mode");
34
35
36
37
38
39
40
41
42
43
44
45 public Object put(String key, Object value)
46 {
47 Validate.notNull(key, "The plugin settings key cannot be null");
48 Validate.isTrue(key.length() <= 255, "The plugin settings key cannot be more than 255 characters");
49 if (isDeveloperMode)
50 {
51 Validate.isTrue(key.length() <= 100, "The plugin settings key cannot be more than 100 characters in developer mode");
52 }
53 if (value == null)
54 {
55 return remove(key);
56 }
57
58 final Object oldValue = get(key);
59 if (value instanceof Properties)
60 {
61 final ByteArrayOutputStream bout = new ByteArrayOutputStream();
62 try
63 {
64
65 final Properties properties = (Properties) value;
66 properties.store(bout, PROPERTIES_IDENTIFIER);
67 putActual(key, new String(bout.toByteArray(), PROPERTIES_ENCODING));
68 }
69 catch (final IOException e)
70 {
71 throw new IllegalArgumentException("Unable to serialize properties", e);
72 }
73 }
74 else if (value instanceof String)
75 {
76 putActual(key, (String) value);
77 }
78 else if (value instanceof List)
79 {
80 final StringBuilder sb = new StringBuilder();
81 sb.append(LIST_IDENTIFIER).append(EscapeUtils.NEW_LINE);
82 for (final Iterator i = ((List) value).iterator(); i.hasNext();)
83 {
84 sb.append(EscapeUtils.escape(i.next().toString()));
85 if (i.hasNext())
86 sb.append(EscapeUtils.NEW_LINE);
87 }
88 putActual(key, sb.toString());
89 }
90 else if (value instanceof Map)
91 {
92 final StringBuilder sb = new StringBuilder();
93 sb.append(MAP_IDENTIFIER).append(EscapeUtils.NEW_LINE);
94 for (final Iterator<Entry> i = ((Map) value).entrySet().iterator(); i.hasNext();)
95 {
96 final Entry entry = i.next();
97 sb.append(EscapeUtils.escape(entry.getKey().toString()));
98 sb.append(EscapeUtils.VERTICAL_TAB);
99 sb.append(EscapeUtils.escape(entry.getValue().toString()));
100 if (i.hasNext())
101 sb.append(EscapeUtils.NEW_LINE);
102 }
103 putActual(key, sb.toString());
104 }
105 else
106 {
107 throw new IllegalArgumentException("Property type: " + value.getClass() + " not supported");
108 }
109 return oldValue;
110 }
111
112
113
114
115
116
117
118
119 public Object get(String key)
120 {
121 Validate.notNull(key, "The plugin settings key cannot be null");
122 if (isDeveloperMode && key.length() > 100)
123 {
124 log.warn("PluginSettings.get with excessive key length: " + key);
125 }
126 final String val = getActual(key);
127 if (val != null && val.startsWith("#" + PROPERTIES_IDENTIFIER))
128 {
129 final Properties p = new Properties();
130 try
131 {
132 p.load(new ByteArrayInputStream(val.getBytes(PROPERTIES_ENCODING)));
133 }
134 catch (final IOException e)
135 {
136 throw new IllegalArgumentException("Unable to deserialize properties", e);
137 }
138 return p;
139 }
140 else if (val != null && val.startsWith(LIST_IDENTIFIER))
141 {
142 final String[] lines = val.split(EscapeUtils.NEW_LINE + "");
143
144
145 final List<String> rawItems = Arrays.asList(lines).subList(1, lines.length);
146
147
148 final List<String> items = Lists.transform(rawItems, new Function<String, String>()
149 {
150 public String apply(String from)
151 {
152 return EscapeUtils.unescape(from);
153 }
154 });
155 return new ArrayList<String>(items);
156 }
157 else if (val != null && val.startsWith(MAP_IDENTIFIER))
158 {
159 String nval = val.substring(MAP_IDENTIFIER.length() + 1);
160 final HashMap<String, String> map = new HashMap<String, String>();
161 final String[] items = nval.split(EscapeUtils.NEW_LINE + "");
162 for (String item : items)
163 {
164 if (item.length() > 0)
165 {
166 String[] pair = item.split(EscapeUtils.VERTICAL_TAB + "");
167 if (pair.length != 2)
168 {
169 log.error("Could not parse map element: << " + item + " >> \n" +
170 "Full list: \n" + nval);
171 }
172 else
173 {
174 map.put(EscapeUtils.unescape(pair[0]),
175 EscapeUtils.unescape(pair[1]));
176 }
177 }
178 }
179
180 return map;
181 }
182 else
183 {
184 return val;
185 }
186 }
187
188
189
190
191
192
193
194 public Object remove(String key)
195 {
196 Validate.notNull(key, "The plugin settings key cannot be null");
197 if (isDeveloperMode && key.length() > 100)
198 {
199 log.warn("PluginSettings.get with excessive key length: " + key);
200 }
201 Object oldValue = get(key);
202 if (oldValue != null)
203 {
204 removeActual(key);
205 }
206 return oldValue;
207 }
208
209
210
211
212
213
214
215 protected abstract void putActual(String key, String val);
216
217
218
219
220
221
222
223 protected abstract String getActual(String key);
224
225
226
227
228
229
230 protected abstract void removeActual(String key);
231
232 }