001: /**
002: *
003: */package net.refractions.udig.catalog;
004:
005: import java.io.File;
006: import java.io.IOException;
007: import java.io.Serializable;
008: import java.io.UnsupportedEncodingException;
009: import java.lang.reflect.Constructor;
010: import java.lang.reflect.Method;
011: import java.net.MalformedURLException;
012: import java.net.URL;
013: import java.net.URLDecoder;
014: import java.net.URLEncoder;
015: import java.util.Collection;
016: import java.util.HashMap;
017: import java.util.List;
018: import java.util.Map;
019:
020: import net.refractions.udig.core.internal.CorePlugin;
021:
022: import org.eclipse.core.runtime.IProgressMonitor;
023: import org.osgi.service.prefs.BackingStoreException;
024: import org.osgi.service.prefs.Preferences;
025:
026: /**
027: * Provides methods for writing IService paramaters to a preference object and for creating services from the preferences.
028: * IResolves that are not services will not be stored
029: *
030: * <p>
031: * {@link #locateService(URL, Map)} can be overridden to not create and add the service to the catalog.
032: * </p>
033: * @author Jesse
034: */
035: public class ServiceParameterPersister {
036: private static final String VALUE_ID = "value"; //$NON-NLS-1$
037: private static final String TYPE_ID = "type"; //$NON-NLS-1$
038: private static final String ENCODING = "UTF-8"; //$NON-NLS-1$
039:
040: protected final ICatalog localCatalog;
041: protected final IServiceFactory serviceFactory;
042: private File reference;
043:
044: public ServiceParameterPersister(final ICatalog localCatalog,
045: final IServiceFactory serviceFactory) {
046: this (localCatalog, serviceFactory, null);
047: }
048:
049: public ServiceParameterPersister(final ICatalog localCatalog,
050: final IServiceFactory serviceFactory, File reference) {
051: super ();
052: this .localCatalog = localCatalog;
053: this .serviceFactory = serviceFactory;
054: this .reference = reference;
055: }
056:
057: public void restore(Preferences node) {
058: try {
059: for (String id : node.childrenNames()) {
060: try {
061: URL url;
062: try {
063: url = decodeURL(id);
064: } catch (UnsupportedEncodingException e) {
065: CatalogPlugin.log(
066: "Could not code preferences URL", e); //$NON-NLS-1$
067: throw new MalformedURLException(e.toString());
068: }
069: Preferences service = node.node(id);
070: String[] keys = service.keys();
071:
072: // BACKWARDS COMPATIBILITY
073: Map<String, Serializable> map = backwardCompatibleRestore(
074: service, keys);
075:
076: String[] nodes = service.childrenNames();
077: for (String childName : nodes) {
078: mapAsObject(service, map, childName);
079: }
080: locateService(url, map);
081: } catch (Throwable t) {
082: CatalogPlugin.log(null, new Exception(t));
083: }
084: }
085:
086: } catch (Throwable t) {
087: CatalogPlugin.log(null, new Exception(t));
088: }
089: }
090:
091: private Map<String, Serializable> backwardCompatibleRestore(
092: Preferences service, String[] keys) {
093: Map<String, Serializable> map = new HashMap<String, Serializable>();
094: for (int j = 0; j < keys.length; j++) {
095: String currentKey = keys[j];
096: map.put(currentKey, service.get(currentKey, null));
097: }
098: return map;
099: }
100:
101: private URL decodeURL(String url) throws MalformedURLException,
102: UnsupportedEncodingException {
103: return new URL(null, URLDecoder.decode(url, ENCODING),
104: CorePlugin.RELAXED_HANDLER);
105: }
106:
107: protected void locateService(URL url, Map<String, Serializable> map) {
108: List<IService> acquire = serviceFactory.acquire(url, map);
109: if (!acquire.isEmpty()) {
110: for (IService serv : acquire) {
111: localCatalog.add(serv);
112: }
113: } else {
114: CatalogPlugin
115: .log(
116: "Nothing was able to be loaded from saved preferences: url=" + url + "" + //$NON-NLS-1$ //$NON-NLS-2$
117: "\nParameters: " + map, null); //$NON-NLS-1$
118: }
119: }
120:
121: private void mapAsObject(Preferences service,
122: Map<String, Serializable> map, String currentKey)
123: throws MalformedURLException {
124: Preferences paramNode = service.node(currentKey);
125: String value = paramNode.get(VALUE_ID, null);
126: try {
127: value = URLDecoder.decode(value, ENCODING);
128: } catch (UnsupportedEncodingException e) {
129: CatalogPlugin.log(
130: "error decoding value, using undecoded value", e); //$NON-NLS-1$
131: }
132: String type = paramNode.get(TYPE_ID, null);
133: try {
134: Class clazz = Class.forName(type);
135:
136: // reference can be null so only decode relative path if reference is not null.
137: // ie assume the URL/File is absolute if reference is null
138: if (reference != null
139: && (URL.class.isAssignableFrom(clazz) || File.class
140: .isAssignableFrom(clazz))) {
141: URL result = URLUtils.constructURL(this .reference,
142: value);
143: if (URL.class.isAssignableFrom(clazz))
144: map.put(currentKey, (Serializable) result);
145: else
146: map.put(currentKey, new File(result.getFile()));
147: return;
148: }
149:
150: try {
151: // try finding the constructor that takes a string
152: Constructor constructor = clazz
153: .getConstructor(new Class[] { String.class });
154: Object object = constructor
155: .newInstance(new Object[] { value });
156: map.put(currentKey, (Serializable) object);
157: } catch (Throwable t) {
158: //failed lets try a setter
159: try {
160: Method[] methods = clazz.getMethods();
161: Method bestMatch = findBestMatch(methods);
162:
163: if (bestMatch != null) {
164: Object obj = clazz.newInstance();
165: bestMatch.invoke(obj, new Object[] { value });
166: map.put(currentKey, (Serializable) obj);
167: }
168: } catch (Throwable t2) {
169: CatalogPlugin
170: .log(
171: "error that occurred when trying use construction with string: " + type + " value= " + value, t); //$NON-NLS-1$ //$NON-NLS-2$
172: CatalogPlugin
173: .log(
174: "error that occurred when use a setter: " + type + " value= " + value, t2); //$NON-NLS-1$//$NON-NLS-2$
175: }
176: }
177:
178: } catch (ClassNotFoundException cnfe) {
179: CatalogPlugin
180: .log(
181: type
182: + " was not able find declared type so we're putting it in to the parameters as a String", null); //$NON-NLS-1$
183: map.put(currentKey, value);
184: }
185: }
186:
187: private Method findBestMatch(Method[] methods) {
188: Method bestMatch = null;
189: for (Method method : methods) {
190: Class<?>[] methodParams = method.getParameterTypes();
191: if (methodParams.length == 1
192: && methodParams[0].isAssignableFrom(String.class)) {
193: // is this a setter or a parse?
194: if (method.getName().startsWith("parse")) { //$NON-NLS-1$
195: if (bestMatch == null) {
196: bestMatch = method;
197: continue;
198: } else {
199: if (bestMatch.getName().startsWith("set")) { //$NON-NLS-1$
200: bestMatch = method;
201: continue;
202: }
203: }
204: }
205:
206: if (method.getName().startsWith("set")) { //$NON-NLS-1$
207: if (bestMatch == null) {
208: bestMatch = method;
209: continue;
210: }
211: }
212: }
213: }
214: return bestMatch;
215: }
216:
217: public void store(IProgressMonitor monitor, Preferences node,
218: Collection<? extends IResolve> resolves)
219: throws BackingStoreException, IOException {
220: clearPreferences(node);
221: for (IResolve member : resolves) {
222: try {
223: if (!CatalogPlugin.getDefault().getPreferenceStore()
224: .getBoolean("SaveTemporaryDataTypes") //$NON-NLS-1$
225: && member.canResolve(ITransientResolve.class))
226: continue;
227: IService service = null;
228: if (member instanceof IGeoResource) {
229: service = ((IGeoResource) member).service(monitor);
230: } else if (member instanceof IService) {
231: service = (IService) member;
232: }
233: // its not a type that we know how to get the parameters from
234: if (service == null)
235: continue;
236:
237: String id;
238: try {
239: id = URLEncoder.encode(service.getIdentifier()
240: .toString(), ENCODING);
241: } catch (UnsupportedEncodingException e1) {
242: // should never happen
243: CatalogPlugin.log(null, e1);
244: throw new BackingStoreException(e1.toString());
245: }
246:
247: Preferences serviceNode = node.node(id);
248:
249: for (Map.Entry<String, Serializable> entry : service
250: .getConnectionParams().entrySet()) {
251: String key = entry.getKey().toString();
252:
253: Serializable object = entry.getValue();
254: URL url = null;
255: if (object instanceof URL) {
256: url = (URL) object;
257: } else if (object instanceof File) {
258: url = ((File) object).toURL();
259: }
260:
261: String value;
262: // if reference is null then we can only encode the absolute path
263: if (reference != null && url != null) {
264: URL relativeURL = URLUtils.toRelativePath(
265: this .reference, url);
266: value = relativeURL.toString();
267: } else {
268: value = object == null ? null : object
269: .toString();
270: }
271:
272: if (value != null) {
273: value = URLEncoder.encode(value, ENCODING);
274: Preferences paramNode = serviceNode.node(key);
275: paramNode.put(VALUE_ID, value);
276: paramNode.put(TYPE_ID, object.getClass()
277: .getName());
278: }
279: }
280: if (serviceNode.keys().length > 0)
281: serviceNode.flush();
282: monitor.worked(1);
283: } catch (RuntimeException e) {
284: CatalogPlugin.log(
285: "Error storing: " + member.getIdentifier(), e); //$NON-NLS-1$
286: }
287: }
288: node.flush();
289: }
290:
291: private void clearPreferences(Preferences node)
292: throws BackingStoreException {
293: for (String name : node.childrenNames()) {
294: Preferences child = node.node(name);
295: child.removeNode();
296: }
297: }
298:
299: }
|