/*
* HelpTOCLoader.java - Help table of contents loader
* :tabSize=4:indentSize=4:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 1999, 2004 Slava Pestov
* Copyright (C) 2016 Eric Le Lay
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.gjt.sp.jedit.help;
//{{{ Imports
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.Stack;
import javax.swing.tree.DefaultMutableTreeNode;
import org.gjt.sp.jedit.EditPlugin;
import org.gjt.sp.jedit.MiscUtilities;
import org.gjt.sp.jedit.jEdit;
import org.gjt.sp.util.Log;
import org.gjt.sp.util.StandardUtilities;
import org.gjt.sp.util.XMLUtilities;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
//}}}
/**
* Help table-of-contents loader.
*
* <p>Code for loading and constructing the table of contents (TOC).
* Doesn't refresh when plugins are (un)loaded: you'll have to call
* {@link #createTOC()} again yourself.</p>
*
* <p>Don't keep {@link HelpTOCLoader} instances between loads:
* use it and forget it.</p>
**/
public class HelpTOCLoader {
//{{{ HelpTOCLoader constructor
public HelpTOCLoader(Map<String, DefaultMutableTreeNode> nodes, String baseURL) {
this.nodes = nodes;
this.baseURL = baseURL;
} //}}}
//{{{ HelpNode class
/**
* a TOC item: href and title.
*/
public static class HelpNode
{
public final String href, title;
//{{{ HelpNode constructor
HelpNode(String href, String title)
{
this.href = href;
this.title = title;
} //}}}
//{{{ toString() method
public String toString()
{
return title;
} //}}}
} //}}}
//{{{ createTOC() method
/**
* Load the table of contents.
* Performs synchronous IO, so you don't want to call it from the GUI thread.
* @return the TOC tree model as a {@link DefaultMutableTreeNode}.
* User objects are {@link HelpNode} instances.
*/
public DefaultMutableTreeNode createTOC()
{
EditPlugin[] plugins = jEdit.getPlugins();
Arrays.sort(plugins,new PluginCompare());
DefaultMutableTreeNode tocRoot = new DefaultMutableTreeNode();
tocRoot.add(createNode("welcome.html",
jEdit.getProperty("helpviewer.toc.welcome")));
tocRoot.add(createNode("README.txt",
jEdit.getProperty("helpviewer.toc.readme")));
tocRoot.add(createNode("CHANGES.txt",
jEdit.getProperty("helpviewer.toc.changes")));
tocRoot.add(createNode("TODO.txt",
jEdit.getProperty("helpviewer.toc.todo")));
tocRoot.add(createNode("COPYING.txt",
jEdit.getProperty("helpviewer.toc.copying")));
tocRoot.add(createNode("COPYING.DOC.txt",
jEdit.getProperty("helpviewer.toc.copying-doc")));
tocRoot.add(createNode("Apache.LICENSE.txt",
jEdit.getProperty("helpviewer.toc.copying-apache")));
tocRoot.add(createNode("COPYING.PLUGINS.txt",
jEdit.getProperty("helpviewer.toc.copying-plugins")));
loadTOC(tocRoot,"whatsnew/toc.xml");
loadTOC(tocRoot,"users-guide/toc.xml");
loadTOC(tocRoot,"FAQ/toc.xml");
DefaultMutableTreeNode pluginTree = new DefaultMutableTreeNode(
jEdit.getProperty("helpviewer.toc.plugins"),true);
for (EditPlugin plugin : plugins)
{
String name = plugin.getClassName();
String docs = jEdit.getProperty("plugin." + name + ".docs");
String label = jEdit.getProperty("plugin." + name + ".name");
if (label != null && docs != null)
{
String path = plugin.getPluginJAR().getClassLoader().getResourceAsPath(docs);
pluginTree.add(createNode(path, label));
}
}
if(pluginTree.getChildCount() != 0)
{
tocRoot.add(pluginTree);
}
loadTOC(tocRoot,"api/toc.xml");
return tocRoot;
} //}}}
//{{{ createNode() method
private DefaultMutableTreeNode createNode(String href, String title)
{
DefaultMutableTreeNode node = new DefaultMutableTreeNode(
new HelpTOCLoader.HelpNode(href,title),true);
if(nodes!=null)
{
nodes.put(href,node);
}
return node;
} //}}}
//{{{ loadTOC() method
private void loadTOC(DefaultMutableTreeNode root, String path)
{
TOCHandler h = new TOCHandler(root,MiscUtilities.getParentOfPath(path));
try
{
XMLUtilities.parseXML(
new URL(baseURL + '/' + path).openStream(), h);
}
catch(FileNotFoundException e)
{
/* it is acceptable only for the API TOC :
the user can choose not to install them
*/
if("api/toc.xml".equals(path))
{
Log.log(Log.NOTICE,this,
"The API docs for jEdit will not be available (reinstall jEdit if you want them)");
root.add(
createNode("http://www.jedit.org/api/overview-summary.html",
jEdit.getProperty("helpviewer.toc.online-apidocs")));
}
else
{
Log.log(Log.ERROR,this,e);
}
}
catch(IOException e)
{
Log.log(Log.ERROR,this,e);
}
} //}}}
//{{{ Private members
private Map<String, DefaultMutableTreeNode> nodes;
private String baseURL;
//}}}
//{{{ TOCHandler class
class TOCHandler extends DefaultHandler
{
String dir;
//{{{ TOCHandler constructor
TOCHandler(DefaultMutableTreeNode root, String dir)
{
nodes = new Stack<DefaultMutableTreeNode>();
node = root;
this.dir = dir;
} //}}}
//{{{ characters() method
public void characters(char[] c, int off, int len)
{
if(tag.equals("TITLE"))
{
boolean firstNonWhitespace = false;
for(int i = 0; i < len; i++)
{
char ch = c[off + i];
if (!firstNonWhitespace && Character.isWhitespace(ch)) continue;
firstNonWhitespace = true;
title.append(ch);
}
}
} //}}}
//{{{ startElement() method
public void startElement(String uri, String localName,
String name, Attributes attrs)
{
tag = name;
if (name.equals("ENTRY"))
href = attrs.getValue("HREF");
} //}}}
//{{{ endElement() method
public void endElement(String uri, String localName, String name)
{
if(name == null)
return;
if(name.equals("TITLE"))
{
DefaultMutableTreeNode newNode = createNode(
dir + href,title.toString());
node.add(newNode);
nodes.push(node);
node = newNode;
title.setLength(0);
}
else if(name.equals("ENTRY"))
{
node = nodes.pop();
href = null;
}
} //}}}
//{{{ Private members
private String tag;
private StringBuilder title = new StringBuilder();
private String href;
private DefaultMutableTreeNode node;
private Stack<DefaultMutableTreeNode> nodes;
//}}}
} //}}}
//{{{ PluginCompare class
static class PluginCompare implements Comparator<EditPlugin>
{
public int compare(EditPlugin p1, EditPlugin p2)
{
return StandardUtilities.compareStrings(
jEdit.getProperty("plugin." + p1.getClassName() + ".name"),
jEdit.getProperty("plugin." + p2.getClassName() + ".name"),
true);
}
} //}}}
}