]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2000, 2011 IBM Corporation and others. | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * IBM Corporation - initial API and implementation | |
10 | *******************************************************************************/ | |
11 | package org.eclipse.jdt.internal.corext.util; | |
12 | ||
13 | import java.io.File; | |
14 | import java.io.FileInputStream; | |
15 | import java.io.FileOutputStream; | |
16 | import java.io.IOException; | |
17 | import java.io.InputStreamReader; | |
18 | import java.io.OutputStream; | |
19 | import java.util.Collection; | |
20 | import java.util.Hashtable; | |
21 | import java.util.Iterator; | |
22 | import java.util.LinkedHashMap; | |
23 | import java.util.Map; | |
24 | import java.util.Set; | |
25 | ||
26 | import javax.xml.parsers.DocumentBuilder; | |
27 | import javax.xml.parsers.DocumentBuilderFactory; | |
28 | import javax.xml.parsers.ParserConfigurationException; | |
29 | import javax.xml.transform.OutputKeys; | |
30 | import javax.xml.transform.Transformer; | |
31 | import javax.xml.transform.TransformerException; | |
32 | import javax.xml.transform.TransformerFactory; | |
33 | import javax.xml.transform.TransformerFactoryConfigurationError; | |
34 | import javax.xml.transform.dom.DOMSource; | |
35 | import javax.xml.transform.stream.StreamResult; | |
36 | ||
37 | import org.xml.sax.InputSource; | |
38 | import org.xml.sax.SAXException; | |
39 | import org.xml.sax.helpers.DefaultHandler; | |
40 | ||
41 | import org.w3c.dom.Document; | |
42 | import org.w3c.dom.Element; | |
43 | import org.w3c.dom.Node; | |
44 | import org.w3c.dom.NodeList; | |
45 | ||
46 | import org.eclipse.core.runtime.CoreException; | |
47 | import org.eclipse.core.runtime.IPath; | |
48 | import org.eclipse.core.runtime.IStatus; | |
49 | ||
50 | import org.eclipse.jdt.internal.corext.CorextMessages; | |
51 | ||
52 | import org.eclipse.jdt.internal.ui.JavaPlugin; | |
53 | import org.eclipse.jdt.internal.ui.JavaUIException; | |
54 | import org.eclipse.jdt.internal.ui.JavaUIStatus; | |
55 | import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; | |
56 | ||
57 | /** | |
58 | * History stores a list of key, object pairs. The list is bounded at size | |
59 | * MAX_HISTORY_SIZE. If the list exceeds this size the eldest element is removed | |
60 | * from the list. An element can be added/renewed with a call to <code>accessed(Object)</code>. | |
61 | * | |
62 | * The history can be stored to/loaded from an xml file. | |
63 | */ | |
64 | public abstract class History { | |
65 | ||
66 | private static final String DEFAULT_ROOT_NODE_NAME= "histroyRootNode"; //$NON-NLS-1$ | |
67 | private static final String DEFAULT_INFO_NODE_NAME= "infoNode"; //$NON-NLS-1$ | |
68 | private static final int MAX_HISTORY_SIZE= 60; | |
69 | ||
70 | private static JavaUIException createException(Throwable t, String message) { | |
71 | return new JavaUIException(JavaUIStatus.createError(IStatus.ERROR, message, t)); | |
72 | } | |
73 | ||
74 | private final Map<Object, Object> fHistory; | |
75 | private final Hashtable<Object, Integer> fPositions; | |
76 | private final String fFileName; | |
77 | private final String fRootNodeName; | |
78 | private final String fInfoNodeName; | |
79 | ||
80 | public History(String fileName, String rootNodeName, String infoNodeName) { | |
81 | fHistory= new LinkedHashMap<Object, Object>(80, 0.75f, true) { | |
82 | private static final long serialVersionUID= 1L; | |
83 | @Override | |
84 | protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) { | |
85 | return size() > MAX_HISTORY_SIZE; | |
86 | } | |
87 | }; | |
88 | fFileName= fileName; | |
89 | fRootNodeName= rootNodeName; | |
90 | fInfoNodeName= infoNodeName; | |
91 | fPositions= new Hashtable<Object, Integer>(MAX_HISTORY_SIZE); | |
92 | } | |
93 | ||
94 | public History(String fileName) { | |
95 | this(fileName, DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME); | |
96 | } | |
97 | ||
98 | public synchronized void accessed(Object object) { | |
99 | fHistory.put(getKey(object), object); | |
100 | rebuildPositions(); | |
101 | } | |
102 | ||
103 | public synchronized boolean contains(Object object) { | |
104 | return fHistory.containsKey(getKey(object)); | |
105 | } | |
106 | ||
107 | public synchronized boolean containsKey(Object key) { | |
108 | return fHistory.containsKey(key); | |
109 | } | |
110 | ||
111 | public synchronized boolean isEmpty() { | |
112 | return fHistory.isEmpty(); | |
113 | } | |
114 | ||
115 | public synchronized Object remove(Object object) { | |
116 | Object removed= fHistory.remove(getKey(object)); | |
117 | rebuildPositions(); | |
118 | return removed; | |
119 | } | |
120 | ||
121 | public synchronized Object removeKey(Object key) { | |
122 | Object removed= fHistory.remove(key); | |
123 | rebuildPositions(); | |
124 | return removed; | |
125 | } | |
126 | ||
127 | /** | |
128 | * Normalized position in history of object denoted by key. | |
129 | * The position is a value between zero and one where zero | |
130 | * means not contained in history and one means newest element | |
131 | * in history. The lower the value the older the element. | |
132 | * | |
133 | * @param key The key of the object to inspect | |
134 | * @return value in [0.0, 1.0] the lower the older the element | |
135 | */ | |
136 | public synchronized float getNormalizedPosition(Object key) { | |
137 | if (!containsKey(key)) | |
138 | return 0.0f; | |
139 | ||
140 | int pos= fPositions.get(key).intValue() + 1; | |
141 | ||
142 | //containsKey(key) implies fHistory.size()>0 | |
143 | return (float)pos / (float)fHistory.size(); | |
144 | } | |
145 | ||
146 | /** | |
147 | * Absolute position of object denoted by key in the | |
148 | * history or -1 if !containsKey(key). The higher the | |
149 | * newer. | |
150 | * | |
151 | * @param key The key of the object to inspect | |
152 | * @return value between 0 and MAX_HISTORY_SIZE - 1, or -1 | |
153 | */ | |
154 | public synchronized int getPosition(Object key) { | |
155 | if (!containsKey(key)) | |
156 | return -1; | |
157 | ||
158 | return fPositions.get(key).intValue(); | |
159 | } | |
160 | ||
161 | public synchronized void load() { | |
162 | IPath stateLocation= JavaPlugin.getDefault().getStateLocation().append(fFileName); | |
163 | File file= stateLocation.toFile(); | |
164 | if (file.exists()) { | |
165 | InputStreamReader reader= null; | |
166 | try { | |
167 | reader = new InputStreamReader(new FileInputStream(file), "utf-8");//$NON-NLS-1$ | |
168 | load(new InputSource(reader)); | |
169 | } catch (IOException e) { | |
170 | JavaPlugin.log(e); | |
171 | } catch (CoreException e) { | |
172 | JavaPlugin.log(e); | |
173 | } finally { | |
174 | try { | |
175 | if (reader != null) | |
176 | reader.close(); | |
177 | } catch (IOException e) { | |
178 | JavaPlugin.log(e); | |
179 | } | |
180 | } | |
181 | } | |
182 | } | |
183 | ||
184 | public synchronized void save() { | |
185 | IPath stateLocation= JavaPlugin.getDefault().getStateLocation().append(fFileName); | |
186 | File file= stateLocation.toFile(); | |
187 | OutputStream out= null; | |
188 | try { | |
189 | out= new FileOutputStream(file); | |
190 | save(out); | |
191 | } catch (IOException e) { | |
192 | JavaPlugin.log(e); | |
193 | } catch (CoreException e) { | |
194 | JavaPlugin.log(e); | |
195 | } catch (TransformerFactoryConfigurationError e) { | |
196 | // The XML library can be misconficgured (e.g. via | |
197 | // -Djava.endorsed.dirs=C:\notExisting\xerces-2_7_1) | |
198 | JavaPlugin.log(e); | |
199 | } finally { | |
200 | try { | |
201 | if (out != null) { | |
202 | out.close(); | |
203 | } | |
204 | } catch (IOException e) { | |
205 | JavaPlugin.log(e); | |
206 | } | |
207 | } | |
208 | } | |
209 | ||
210 | protected Set<Object> getKeys() { | |
211 | return fHistory.keySet(); | |
212 | } | |
213 | ||
214 | protected Collection<Object> getValues() { | |
215 | return fHistory.values(); | |
216 | } | |
217 | ||
218 | /** | |
219 | * Store <code>Object</code> in <code>Element</code> | |
220 | * | |
221 | * @param object The object to store | |
222 | * @param element The Element to store to | |
223 | */ | |
224 | protected abstract void setAttributes(Object object, Element element); | |
225 | ||
226 | /** | |
227 | * Return a new instance of an Object given <code>element</code> | |
228 | * | |
229 | * @param element The element containing required information to create the Object | |
230 | * @return return a new instance of an Object given <code>element</code> | |
231 | */ | |
232 | protected abstract Object createFromElement(Element element); | |
233 | ||
234 | /** | |
235 | * Get key for object | |
236 | * | |
237 | * @param object The object to calculate a key for, not null | |
238 | * @return The key for object, not null | |
239 | */ | |
240 | protected abstract Object getKey(Object object); | |
241 | ||
242 | private void rebuildPositions() { | |
243 | fPositions.clear(); | |
244 | Collection<Object> values= fHistory.values(); | |
245 | int pos=0; | |
246 | for (Iterator<Object> iter= values.iterator(); iter.hasNext();) { | |
247 | Object element= iter.next(); | |
248 | fPositions.put(getKey(element), new Integer(pos)); | |
249 | pos++; | |
250 | } | |
251 | } | |
252 | ||
253 | private void load(InputSource inputSource) throws CoreException { | |
254 | Element root; | |
255 | try { | |
256 | DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); | |
257 | parser.setErrorHandler(new DefaultHandler()); | |
258 | root = parser.parse(inputSource).getDocumentElement(); | |
259 | } catch (SAXException e) { | |
260 | throw createException(e, Messages.format(CorextMessages.History_error_read, BasicElementLabels.getResourceName(fFileName))); | |
261 | } catch (ParserConfigurationException e) { | |
262 | throw createException(e, Messages.format(CorextMessages.History_error_read, BasicElementLabels.getResourceName(fFileName))); | |
263 | } catch (IOException e) { | |
264 | throw createException(e, Messages.format(CorextMessages.History_error_read, BasicElementLabels.getResourceName(fFileName))); | |
265 | } | |
266 | ||
267 | if (root == null) return; | |
268 | if (!root.getNodeName().equalsIgnoreCase(fRootNodeName)) { | |
269 | return; | |
270 | } | |
271 | NodeList list= root.getChildNodes(); | |
272 | int length= list.getLength(); | |
273 | for (int i= 0; i < length; ++i) { | |
274 | Node node= list.item(i); | |
275 | if (node.getNodeType() == Node.ELEMENT_NODE) { | |
276 | Element type= (Element) node; | |
277 | if (type.getNodeName().equalsIgnoreCase(fInfoNodeName)) { | |
278 | Object object= createFromElement(type); | |
279 | if (object != null) { | |
280 | fHistory.put(getKey(object), object); | |
281 | } | |
282 | } | |
283 | } | |
284 | } | |
285 | rebuildPositions(); | |
286 | } | |
287 | ||
288 | private void save(OutputStream stream) throws CoreException { | |
289 | try { | |
290 | DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance(); | |
291 | DocumentBuilder builder= factory.newDocumentBuilder(); | |
292 | Document document= builder.newDocument(); | |
293 | ||
294 | Element rootElement = document.createElement(fRootNodeName); | |
295 | document.appendChild(rootElement); | |
296 | ||
297 | Iterator<Object> values= getValues().iterator(); | |
298 | while (values.hasNext()) { | |
299 | Object object= values.next(); | |
300 | Element element= document.createElement(fInfoNodeName); | |
301 | setAttributes(object, element); | |
302 | rootElement.appendChild(element); | |
303 | } | |
304 | ||
305 | Transformer transformer=TransformerFactory.newInstance().newTransformer(); | |
306 | transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ | |
307 | transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$ | |
308 | transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ | |
309 | DOMSource source = new DOMSource(document); | |
310 | StreamResult result = new StreamResult(stream); | |
311 | ||
312 | transformer.transform(source, result); | |
313 | } catch (TransformerException e) { | |
314 | throw createException(e, Messages.format(CorextMessages.History_error_serialize, BasicElementLabels.getResourceName(fFileName))); | |
315 | } catch (ParserConfigurationException e) { | |
316 | throw createException(e, Messages.format(CorextMessages.History_error_serialize, BasicElementLabels.getResourceName(fFileName))); | |
317 | } | |
318 | } | |
319 | ||
320 | } |