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 private static final Logger log = LoggerFactory.getLogger(AbstractStringPluginSettings.class);
28
29 private static final String PROPERTIES_ENCODING = "ISO8859_1";
30 private static final String PROPERTIES_IDENTIFIER = "java.util.Properties";
31 private static final String LIST_IDENTIFIER = "#java.util.List";
32 private static final String MAP_IDENTIFIER = "#java.util.Map";
33
34
35
36
37
38
39
40
41 private final boolean isDeveloperMode = Boolean.getBoolean("atlassian.dev.mode");
42
43
44
45
46
47
48
49
50
51
52
53 public Object put(String key, Object value) {
54 Validate.notNull(key, "The plugin settings key cannot be null");
55 Validate.isTrue(key.length() <= 255, "The plugin settings key cannot be more than 255 characters");
56 if (isDeveloperMode) {
57 Validate.isTrue(key.length() <= 100, "The plugin settings key cannot be more than 100 characters in developer mode");
58 }
59 if (value == null) {
60 return remove(key);
61 }
62
63 final Object oldValue = get(key);
64 if (value instanceof Properties) {
65 final ByteArrayOutputStream bout = new ByteArrayOutputStream();
66 try {
67
68 final Properties properties = (Properties) value;
69 properties.store(bout, PROPERTIES_IDENTIFIER);
70 putActual(key, new String(bout.toByteArray(), PROPERTIES_ENCODING));
71 } catch (final IOException e) {
72 throw new IllegalArgumentException("Unable to serialize properties", e);
73 }
74 } else if (value instanceof String) {
75 putActual(key, (String) value);
76 } else if (value instanceof List) {
77 final StringBuilder sb = new StringBuilder();
78 sb.append(LIST_IDENTIFIER).append(EscapeUtils.NEW_LINE);
79 for (final Iterator i = ((List) value).iterator(); i.hasNext(); ) {
80 sb.append(EscapeUtils.escape(i.next().toString()));
81 if (i.hasNext())
82 sb.append(EscapeUtils.NEW_LINE);
83 }
84 putActual(key, sb.toString());
85 } else if (value instanceof Map) {
86 final StringBuilder sb = new StringBuilder();
87 sb.append(MAP_IDENTIFIER).append(EscapeUtils.NEW_LINE);
88 for (final Iterator<Entry> i = ((Map) value).entrySet().iterator(); i.hasNext(); ) {
89 final Entry entry = i.next();
90 sb.append(EscapeUtils.escape(entry.getKey().toString()));
91 sb.append(EscapeUtils.VERTICAL_TAB);
92 sb.append(EscapeUtils.escape(entry.getValue().toString()));
93 if (i.hasNext())
94 sb.append(EscapeUtils.NEW_LINE);
95 }
96 putActual(key, sb.toString());
97 } else {
98 throw new IllegalArgumentException("Property type: " + value.getClass() + " not supported");
99 }
100 return oldValue;
101 }
102
103
104
105
106
107
108
109
110 public Object get(String key) {
111 Validate.notNull(key, "The plugin settings key cannot be null");
112 if (isDeveloperMode && key.length() > 100) {
113 log.warn("PluginSettings.get with excessive key length: {}", key);
114 }
115 final String val = getActual(key);
116 if (val != null && val.startsWith("#" + PROPERTIES_IDENTIFIER)) {
117 final Properties p = new Properties();
118 try {
119 p.load(new ByteArrayInputStream(val.getBytes(PROPERTIES_ENCODING)));
120 } catch (final IOException e) {
121 throw new IllegalArgumentException("Unable to deserialize properties", e);
122 }
123 return p;
124 } else if (val != null && val.startsWith(LIST_IDENTIFIER)) {
125 final String[] lines = val.split(EscapeUtils.NEW_LINE + "");
126
127
128 final List<String> rawItems = Arrays.asList(lines).subList(1, lines.length);
129
130
131 final List<String> items = Lists.transform(rawItems, new Function<String, String>() {
132 public String apply(String from) {
133 return EscapeUtils.unescape(from);
134 }
135 });
136 return new ArrayList<String>(items);
137 } else if (val != null && val.startsWith(MAP_IDENTIFIER)) {
138 String nval = val.substring(MAP_IDENTIFIER.length() + 1);
139 final HashMap<String, String> map = new HashMap<String, String>();
140 final String[] items = nval.split(EscapeUtils.NEW_LINE + "");
141 for (String item : items) {
142 if (item.length() > 0) {
143 int tabLocation = item.indexOf(EscapeUtils.VERTICAL_TAB);
144
145 if (tabLocation == -1) {
146 log.error("Could not parse map element: '{}'\nFull list: '{}'\n", item, nval);
147 } else {
148 String keyPart = item.substring(0, tabLocation);
149 String valuePart = item.substring(tabLocation + 1, item.length());
150 map.put(EscapeUtils.unescape(keyPart),
151 EscapeUtils.unescape(valuePart));
152 }
153 }
154 }
155
156 return map;
157 } else {
158 return val;
159 }
160 }
161
162
163
164
165
166
167
168 public Object remove(String key) {
169 Validate.notNull(key, "The plugin settings key cannot be null");
170 if (isDeveloperMode && key.length() > 100) {
171 log.warn("PluginSettings.get with excessive key length: {}", key);
172 }
173 Object oldValue = get(key);
174 if (oldValue != null) {
175 removeActual(key);
176 }
177 return oldValue;
178 }
179
180
181
182
183
184
185
186 protected abstract void putActual(String key, String val);
187
188
189
190
191
192
193
194 protected abstract String getActual(String key);
195
196
197
198
199
200
201 protected abstract void removeActual(String key);
202
203 }