| /******************************************************************************* |
| * Copyright (c) 2000, 2006 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.corext.util; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.OutputStream; |
| import java.util.Collection; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.transform.OutputKeys; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.TransformerFactoryConfigurationError; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.stream.StreamResult; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| |
| import org.eclipse.jdt.internal.corext.CorextMessages; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.JavaUIException; |
| import org.eclipse.jdt.internal.ui.JavaUIStatus; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * History stores a list of key, object pairs. The list is bounded at size |
| * MAX_HISTORY_SIZE. If the list exceeds this size the eldest element is removed |
| * from the list. An element can be added/renewed with a call to <code>accessed(Object)</code>. |
| * |
| * The history can be stored to/loaded from an xml file. |
| */ |
| public abstract class History { |
| |
| private static final String DEFAULT_ROOT_NODE_NAME= "histroyRootNode"; //$NON-NLS-1$ |
| private static final String DEFAULT_INFO_NODE_NAME= "infoNode"; //$NON-NLS-1$ |
| private static final int MAX_HISTORY_SIZE= 60; |
| |
| private static JavaUIException createException(Throwable t, String message) { |
| return new JavaUIException(JavaUIStatus.createError(IStatus.ERROR, message, t)); |
| } |
| |
| private final Map fHistory; |
| private final Hashtable fPositions; |
| private final String fFileName; |
| private final String fRootNodeName; |
| private final String fInfoNodeName; |
| |
| public History(String fileName, String rootNodeName, String infoNodeName) { |
| fHistory= new LinkedHashMap(80, 0.75f, true) { |
| private static final long serialVersionUID= 1L; |
| protected boolean removeEldestEntry(Map.Entry eldest) { |
| return size() > MAX_HISTORY_SIZE; |
| } |
| }; |
| fFileName= fileName; |
| fRootNodeName= rootNodeName; |
| fInfoNodeName= infoNodeName; |
| fPositions= new Hashtable(MAX_HISTORY_SIZE); |
| } |
| |
| public History(String fileName) { |
| this(fileName, DEFAULT_ROOT_NODE_NAME, DEFAULT_INFO_NODE_NAME); |
| } |
| |
| public synchronized void accessed(Object object) { |
| fHistory.put(getKey(object), object); |
| rebuildPositions(); |
| } |
| |
| public synchronized boolean contains(Object object) { |
| return fHistory.containsKey(getKey(object)); |
| } |
| |
| public synchronized boolean containsKey(Object key) { |
| return fHistory.containsKey(key); |
| } |
| |
| public synchronized boolean isEmpty() { |
| return fHistory.isEmpty(); |
| } |
| |
| public synchronized Object remove(Object object) { |
| Object removed= fHistory.remove(getKey(object)); |
| rebuildPositions(); |
| return removed; |
| } |
| |
| public synchronized Object removeKey(Object key) { |
| Object removed= fHistory.remove(key); |
| rebuildPositions(); |
| return removed; |
| } |
| |
| /** |
| * Normalized position in history of object denoted by key. |
| * The position is a value between zero and one where zero |
| * means not contained in history and one means newest element |
| * in history. The lower the value the older the element. |
| * |
| * @param key The key of the object to inspect |
| * @return value in [0.0, 1.0] the lower the older the element |
| */ |
| public synchronized float getNormalizedPosition(Object key) { |
| if (!containsKey(key)) |
| return 0.0f; |
| |
| int pos= ((Integer)fPositions.get(key)).intValue() + 1; |
| |
| //containsKey(key) implies fHistory.size()>0 |
| return (float)pos / (float)fHistory.size(); |
| } |
| |
| /** |
| * Absolute position of object denoted by key in the |
| * history or -1 if !containsKey(key). The higher the |
| * newer. |
| * |
| * @param key The key of the object to inspect |
| * @return value between 0 and MAX_HISTORY_SIZE - 1, or -1 |
| */ |
| public synchronized int getPosition(Object key) { |
| if (!containsKey(key)) |
| return -1; |
| |
| return ((Integer)fPositions.get(key)).intValue(); |
| } |
| |
| public synchronized void load() { |
| IPath stateLocation= JavaPlugin.getDefault().getStateLocation().append(fFileName); |
| File file= new File(stateLocation.toOSString()); |
| if (file.exists()) { |
| InputStreamReader reader= null; |
| try { |
| reader = new InputStreamReader(new FileInputStream(file), "utf-8");//$NON-NLS-1$ |
| load(new InputSource(reader)); |
| } catch (IOException e) { |
| JavaPlugin.log(e); |
| } catch (CoreException e) { |
| JavaPlugin.log(e); |
| } finally { |
| try { |
| if (reader != null) |
| reader.close(); |
| } catch (IOException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| } |
| } |
| |
| public synchronized void save() { |
| IPath stateLocation= JavaPlugin.getDefault().getStateLocation().append(fFileName); |
| File file= new File(stateLocation.toOSString()); |
| OutputStream out= null; |
| try { |
| out= new FileOutputStream(file); |
| save(out); |
| } catch (IOException e) { |
| JavaPlugin.log(e); |
| } catch (CoreException e) { |
| JavaPlugin.log(e); |
| } catch (TransformerFactoryConfigurationError e) { |
| // The XML library can be misconficgured (e.g. via |
| // -Djava.endorsed.dirs=C:\notExisting\xerces-2_7_1) |
| JavaPlugin.log(e); |
| } finally { |
| try { |
| if (out != null) { |
| out.close(); |
| } |
| } catch (IOException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| } |
| |
| protected Set getKeys() { |
| return fHistory.keySet(); |
| } |
| |
| protected Collection getValues() { |
| return fHistory.values(); |
| } |
| |
| /** |
| * Store <code>Object</code> in <code>Element</code> |
| * |
| * @param object The object to store |
| * @param element The Element to store to |
| */ |
| protected abstract void setAttributes(Object object, Element element); |
| |
| /** |
| * Return a new instance of an Object given <code>element</code> |
| * |
| * @param element The element containing required information to create the Object |
| */ |
| protected abstract Object createFromElement(Element element); |
| |
| /** |
| * Get key for object |
| * |
| * @param object The object to calculate a key for, not null |
| * @return The key for object, not null |
| */ |
| protected abstract Object getKey(Object object); |
| |
| private void rebuildPositions() { |
| fPositions.clear(); |
| Collection values= fHistory.values(); |
| int pos=0; |
| for (Iterator iter= values.iterator(); iter.hasNext();) { |
| Object element= iter.next(); |
| fPositions.put(getKey(element), new Integer(pos)); |
| pos++; |
| } |
| } |
| |
| private void load(InputSource inputSource) throws CoreException { |
| Element root; |
| try { |
| DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| root = parser.parse(inputSource).getDocumentElement(); |
| } catch (SAXException e) { |
| throw createException(e, Messages.format(CorextMessages.History_error_read, fFileName)); |
| } catch (ParserConfigurationException e) { |
| throw createException(e, Messages.format(CorextMessages.History_error_read, fFileName)); |
| } catch (IOException e) { |
| throw createException(e, Messages.format(CorextMessages.History_error_read, fFileName)); |
| } |
| |
| if (root == null) return; |
| if (!root.getNodeName().equalsIgnoreCase(fRootNodeName)) { |
| return; |
| } |
| NodeList list= root.getChildNodes(); |
| int length= list.getLength(); |
| for (int i= 0; i < length; ++i) { |
| Node node= list.item(i); |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| Element type= (Element) node; |
| if (type.getNodeName().equalsIgnoreCase(fInfoNodeName)) { |
| Object object= createFromElement(type); |
| fHistory.put(getKey(object), object); |
| } |
| } |
| } |
| rebuildPositions(); |
| } |
| |
| private void save(OutputStream stream) throws CoreException { |
| try { |
| DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance(); |
| DocumentBuilder builder= factory.newDocumentBuilder(); |
| Document document= builder.newDocument(); |
| |
| Element rootElement = document.createElement(fRootNodeName); |
| document.appendChild(rootElement); |
| |
| Iterator values= getValues().iterator(); |
| while (values.hasNext()) { |
| Object object= values.next(); |
| Element element= document.createElement(fInfoNodeName); |
| setAttributes(object, element); |
| rootElement.appendChild(element); |
| } |
| |
| Transformer transformer=TransformerFactory.newInstance().newTransformer(); |
| transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ |
| transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$ |
| transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ |
| DOMSource source = new DOMSource(document); |
| StreamResult result = new StreamResult(stream); |
| |
| transformer.transform(source, result); |
| } catch (TransformerException e) { |
| throw createException(e, Messages.format(CorextMessages.History_error_serialize, fFileName)); |
| } catch (ParserConfigurationException e) { |
| throw createException(e, Messages.format(CorextMessages.History_error_serialize, fFileName)); |
| } |
| } |
| |
| } |