Skip to main content
summaryrefslogtreecommitdiffstats
blob: ee68312b905998313440b1293b78e63a2e120720 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package org.eclipse.help.internal.validation;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;

import javax.xml.parsers.ParserConfigurationException;

import org.eclipse.help.IHelpResource;
import org.eclipse.help.ILink;
import org.eclipse.help.IToc;
import org.eclipse.help.IUAElement;
import org.eclipse.help.internal.protocols.HelpURLConnection;
import org.eclipse.help.internal.toc.TocContribution;
import org.eclipse.help.internal.toc.TocFile;
import org.eclipse.help.internal.toc.TocFileParser;
import org.xml.sax.SAXException;

/*******************************************************************************
 * Copyright (c) 2007, 2015 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
 *******************************************************************************/

public class TocValidator {

	private static final boolean DEBUG = false;

	private HashMap<String, Object> processedTocs;
	private TocFileParser parser;

	public static class BrokenLink {
		private String tocID;
		private String href;
		private BrokenLink(String tocID, String href) {
			this.tocID = tocID;
			this.href = href;
		}
		public String getTocID() {
			return tocID; }
		public String getHref() {
			return href; }
	}

	public static abstract class Filter {
	     abstract public boolean isIncluded(String href);
	}

	public static class PassThroughFilter extends Filter {
		@Override
		public boolean isIncluded(String href) {
			return true;
		}
	}

	/**
	 * Checks the validity of all <code>href</code> attributes on <code>topic</code> elements in the toc and the
	 * <code>topic</code> attribute on the <code>toc</code> element if there is one. Also checks validity of any
	 * nested tocs.
	 * @param hrefs gives the list of paths to toc files to validate including the plug-in id
	 * (i.e. "/&lt;plug-in id&gt;/&lt;path&gt;/&lt;file&gt;")
	 * @return An ArrayList of BrokenLink objects in the toc. If no broken links are found, an empty ArrayList
	 * is returned.
	 * @throws IOException
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 */
	public static ArrayList<BrokenLink> validate(String[] hrefs) throws IOException, SAXException, ParserConfigurationException{
		return filteredValidate(hrefs, new PassThroughFilter());
	}

	public static ArrayList<BrokenLink> filteredValidate (String[] hrefs, Filter filter) throws IOException, SAXException, ParserConfigurationException{
		TocValidator v = new TocValidator();
		ArrayList<BrokenLink> result = new ArrayList<>();
		for (int i = 0; i < hrefs.length; i++)
			v.processToc(hrefs[i], null, result, filter);
		return result;
	}

	private TocValidator() {
		processedTocs = new HashMap<>();
		parser = new TocFileParser();
	}

	/* Checks validity of all links in a given toc. If all links are valid, an empty ArrayList is returned.
	 * Otherwise an ArrayList of BrokenLink objects is returned.
	 */
	private void processToc(String href, String plugin, ArrayList<BrokenLink> result, Filter filter)
	               throws IOException, SAXException, ParserConfigurationException {
		String path;
		if (href.startsWith("/")) { //$NON-NLS-1$
			href = href.substring(1);
			int index = href.indexOf("/"); //$NON-NLS-1$
			if (index == -1)
				throw new IOException("Invalid parameters supplied to the validate method."); //$NON-NLS-1$
			plugin = href.substring(0, index);
			path = href.substring(index+1);
		} else {
			path = href;
		}
		if (plugin == null)
			throw new IOException("Invalid parameters supplied to the validate method."); //$NON-NLS-1$
		String key = "/" + plugin + "/" + path; //$NON-NLS-1$ //$NON-NLS-2$
		if (processedTocs.get(key) != null) {
			if (DEBUG)
				System.out.println("Skipping toc because it has already been validated: " + key); //$NON-NLS-1$
			return;
		}
		if (DEBUG)
			System.out.println("Starting toc: " + key); //$NON-NLS-1$
		processedTocs.put(key, new Object());
		TocContribution contribution = parser.parse(new TocFile(plugin,path, true, "en", null, null)); //$NON-NLS-1$
		process(contribution.getToc(), plugin, path, result, filter);
	}

	/* Checks validity of all links in the given IUAElement and recursively calls itself to check all children.
	 * If there are any links to other tocs, calls the processToc method to validate them. If any broken links
	 * are found, an appropriate BrokenLink object will be added to the result ArrayList.
	 */
	private void process(IUAElement element, String plugin, String path, ArrayList<BrokenLink> result, Filter filter) throws SAXException, ParserConfigurationException {
		String href;
		if (element instanceof ILink) {
			href = ((ILink)element).getToc();
			try {
				processToc(href, plugin, result, filter);
			} catch (IOException e) {
				result.add(new BrokenLink("/" + plugin + "/" + path, href)); //$NON-NLS-1$ //$NON-NLS-2$
			}
		}
		else if (element instanceof IHelpResource) {
			if (element instanceof IToc)
				href = ((IToc)element).getTopic(null).getHref();
			else
				href = ((IHelpResource)element).getHref();
			if (href != null && filter.isIncluded(href))
				if (!checkLink(href, plugin)) {
					result.add(new BrokenLink("/" + plugin + "/" + path, href)); //$NON-NLS-1$ //$NON-NLS-2$
				}
		}
		IUAElement [] children = element.getChildren();
		for (int i = 0; i < children.length; i++)
			process(children[i], plugin, path, result, filter);
	}

	/* Checks validity of a given link from a toc in a given plug-in.
	 * returns true if the link is valid.
	 */
	private boolean checkLink(String href, String plugin) {
		if (href.startsWith("http")) { //$NON-NLS-1$
			if (DEBUG)
				System.out.println("    Skipping href: " + href); //$NON-NLS-1$
			return true;
		}
		if (DEBUG)
			System.out.print("    Checking href: " + href + "..."); //$NON-NLS-1$ //$NON-NLS-2$
		boolean result = true;
		InputStream i = null;
		try {
			HelpURLConnection c = new HelpURLConnection(createURL(href, plugin));
			if ((i = c.getInputStream()) == null)
				result = false;
		} catch (Exception e) {
			result = false;
		}
		if (i != null) {
			try { i.close(); } catch(Exception e) { }
			i = null;
		}
		if (DEBUG)
			System.out.println(result?"pass":"fail"); //$NON-NLS-1$ //$NON-NLS-2$
		return result;
	}

	// Builds a URL for a given plug-in/href to create a HelpURLConnection
	private URL createURL(String href, String plugin) throws MalformedURLException {
		StringBuilder url = new StringBuilder("file:/"); //$NON-NLS-1$
		url.append(plugin);
		url.append("/"); //$NON-NLS-1$
		url.append(href);
		return new URL(url.toString());
	}
}

Back to the top