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