1 /*******************************************************************************
2  * Copyright (c) 2010, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 
15 package org.eclipse.help.internal.base.scope;
16 
17 import java.util.ArrayList;
18 import java.util.List;
19 
20 import org.eclipse.help.IIndexEntry;
21 import org.eclipse.help.IIndexEntry2;
22 import org.eclipse.help.IIndexSee;
23 import org.eclipse.help.IToc;
24 import org.eclipse.help.ITopic;
25 import org.eclipse.help.IUAElement;
26 import org.eclipse.help.base.AbstractHelpScope;
27 import org.eclipse.help.internal.UAElement;
28 import org.eclipse.help.internal.index.Index;
29 import org.eclipse.help.internal.index.IndexSee;
30 
31 /**
32  * Utilities to test for enabled topics, index entries etc.
33  */
34 
35 public class ScopeUtils {
36 
37 
38 	/*
39 	 * Function to determine whether a topic should be shown in the toc.
40 	 * For hierarchical scopes the element itself must be in scope.
41 	 * For non hierarchical scopes if any child is in scope the element should show.
42 	 * A toc with no in scope topics does not display
43 	 */
showInTree(IToc toc, AbstractHelpScope scope)44 	public static boolean showInTree(IToc toc, AbstractHelpScope scope) {
45 		if (scope.isHierarchicalScope() && !scope.inScope(toc)) {
46 			return false;
47 		}
48 		return hasInScopeDescendent(toc, scope);
49 
50 	}
51 
52 	/*
53 	 * Function to determine whether a topic should be shown in the toc.
54 	 * For hierarchical scopes the element itself must be in scope.
55 	 * For non hierarchical scopes if any child is in scope the element should show.
56 	 * Leaf topics with no href do not show in the toc
57 	 */
showInTree(ITopic topic, AbstractHelpScope scope)58 	public static boolean showInTree(ITopic topic, AbstractHelpScope scope) {
59 		if (scope.inScope(topic)) {
60 			return (topic.getHref() != null) || hasInScopeDescendent(topic, scope);
61 		}
62 		return !scope.isHierarchicalScope() && hasInScopeDescendent(topic, scope);
63 	}
64 
65 	/*
66 	 * Function to determine whether an entry should be shown in the index.
67 	 * For hierarchical scopes the element itself must be in scope.
68 	 * For non hierarchical scopes if any child is in scope the element should show.
69 	 * An entry with no topic descendants does not display
70 	 */
showInTree(IIndexEntry entry, AbstractHelpScope scope)71 	public static boolean showInTree(IIndexEntry entry, AbstractHelpScope scope) {
72 		if (scope.isHierarchicalScope() && !scope.inScope(entry)) {
73 			return false;
74 		}
75 		return hasInScopeDescendent(entry, scope);
76 	}
77 
78 	/*
79 	 * Returns true if one of the children meets the conditions
80 	 * necessary to be shown in the Toc tree
81 	 */
hasInScopeDescendent(ITopic topic, AbstractHelpScope scope)82 	public static boolean hasInScopeDescendent(ITopic topic, AbstractHelpScope scope) {
83 		ITopic[] subtopics = topic.getSubtopics();
84 		for (ITopic subtopic : subtopics) {
85 			if (showInTree(subtopic, scope)) {
86 				return true;
87 			}
88 		}
89 		return false;
90 	}
91 
92 	/*
93 	 * Returns true if one of the children meets the conditions
94 	 * necessary to be shown in the Toc tree
95 	 */
hasInScopeDescendent(IToc toc, AbstractHelpScope scope)96 	public static boolean hasInScopeDescendent(IToc toc, AbstractHelpScope scope) {
97 		ITopic[] topics = toc.getTopics();
98 		for (ITopic topic : topics) {
99 			if (showInTree(topic, scope)) {
100 				return true;
101 			}
102 		}
103 		return false;
104 	}
105 
106 	/*
107 	 * Returns true if one of the children meets the conditions
108 	 * necessary to be shown in the Index
109 	 */
hasInScopeDescendent(IIndexEntry entry, AbstractHelpScope scope)110 	public static boolean hasInScopeDescendent(IIndexEntry entry,
111 			AbstractHelpScope scope) {
112 		ITopic[] topics = entry.getTopics();
113 		for (ITopic topic : topics) {
114 			if (showInTree(topic, scope)) {
115 				return true;
116 			}
117 		}
118 		IIndexEntry[] entries = entry.getSubentries();
119 		for (IIndexEntry innerEntry : entries) {
120 			if (showInTree(innerEntry, scope)) {
121 				return true;
122 			}
123 		}
124 		if (entry instanceof IIndexEntry2) {
125 			IIndexSee[] sees = ((IIndexEntry2)entry).getSees();
126 			for (IIndexSee see : sees) {
127 				if (showInTree(see, scope)) {
128 					return true;
129 				}
130 			}
131 		}
132 		return false;
133 	}
134 
hasInScopeTarget(IIndexSee see, AbstractHelpScope scope)135 	public static boolean hasInScopeTarget(IIndexSee see, AbstractHelpScope scope) {
136 		if (see instanceof IndexSee) {
137 			IndexSee indexSee = (IndexSee)see;
138 			UAElement ancestor = indexSee.getParentElement();
139 			while (!(ancestor instanceof Index)) {
140 				if (ancestor == null) {
141 					return true;
142 				}
143 				ancestor = ancestor.getParentElement();
144 			}
145 			IIndexEntry target = ((Index)ancestor).getSeeTarget(indexSee);
146 			if (target == null) {
147 				return false;
148 			}
149 			return showInTree(target, scope);
150 		}
151 		return false;
152 	}
153 
showInTree(IIndexSee see, AbstractHelpScope scope)154 	public static boolean showInTree(IIndexSee see, AbstractHelpScope scope) {
155 		if (scope.isHierarchicalScope() && !scope.inScope(see)) {
156 			return false;
157 		}
158 		if (see instanceof IndexSee) {
159 			IndexSee indexSee = (IndexSee)see;
160 			UAElement ancestor = indexSee.getParentElement();
161 			while (!(ancestor instanceof Index)) {
162 				if (ancestor == null) {
163 					return true;
164 				}
165 				ancestor = ancestor.getParentElement();
166 			}
167 			IIndexEntry target = ((Index)ancestor).getSeeTarget(indexSee);
168 			if (target == null) {
169 				return false;
170 			}
171 			return showInTree(target, scope);
172 		}
173 		return false;
174 	}
175 
176 	/**
177 	 * Filter out any disabled entries from an array
178 	 * @param entries an array of entries
179 	 * @param scope
180 	 * @return an array containing only those entries which are enabled
181 	 */
inScopeEntries(IIndexEntry[] entries, AbstractHelpScope scope)182 	public static IIndexEntry[] inScopeEntries(IIndexEntry[] entries, AbstractHelpScope scope) {
183 		for (int i=0;i<entries.length;++i) {
184 			if (!scope.inScope(entries[i])) {
185 				List<IIndexEntry> list = new ArrayList<>(entries.length);
186 				for (int j=0;j<entries.length;++j) {
187 					if (j < i || scope.inScope(entries[j])) {
188 						list.add(entries[j]);
189 					}
190 				}
191 				return list.toArray(new IIndexEntry[list.size()]);
192 			}
193 		}
194 		return entries;
195 	}
196 
197 	/**
198 	 * Filter out any disabled topics from an array
199 	 * @param topics an array of topics
200 	 * @param scope
201 	 * @return an array containing only those topics which are enabled
202 	 */
inScopeTopics(ITopic[] topics, AbstractHelpScope scope)203 	public static ITopic[] inScopeTopics(ITopic[] topics, AbstractHelpScope scope) {
204 		for (int i=0;i<topics.length;++i) {
205 			if (!scope.inScope(topics[i])) {
206 				List<ITopic> list = new ArrayList<>(topics.length);
207 				for (int j=0;j<topics.length;++j) {
208 					if (j < i || scope.inScope(topics[j])) {
209 						list.add(topics[j]);
210 					}
211 				}
212 				return list.toArray(new ITopic[list.size()]);
213 			}
214 		}
215 		return topics;
216 	}
217 
hasInScopeChildren(IUAElement element, AbstractHelpScope scope)218 	public static boolean hasInScopeChildren(IUAElement element,
219 			AbstractHelpScope scope) {
220 		if (element instanceof IToc) {
221 			return hasInScopeDescendent((IToc)element, scope);
222 		}
223 		if (element instanceof ITopic) {
224 			return hasInScopeDescendent((ITopic)element, scope);
225 		}
226 		if (element instanceof IIndexEntry) {
227 			return hasInScopeDescendent((IIndexEntry) element, scope);
228 		}
229 		if (element instanceof IIndexSee) {
230 			return hasInScopeTarget((IIndexSee) element, scope);
231 		}
232 		return false;
233 	}
234 
235 }
236