blob: 7f07a3c8c82cec21ba982108ce1292fd3aa397e4 [file] [log] [blame]
package org.eclipse.help.internal.navigation;
/*
* Licensed Materials - Property of IBM,
* WebSphere Studio Workbench
* (c) Copyright IBM Corp 2000
*/
import java.util.*;
import org.eclipse.help.internal.contributors.*;
import org.eclipse.help.internal.contributions.*;
import org.eclipse.help.internal.contributions.xml.*;
/**
* Builder for the contribution hierarchy using the insert contributions.
*/
public class InfosetBuilder {
// Contributions data
private Iterator infosets;
private Iterator topics;
private Iterator actions;
// Topics data indexed by ID
private Map topicNodeMap = new HashMap(/* of <topic> Node */);
private Map topicSetNodeMap = new HashMap(/* of <topics> Node */);
// Infosets
private Map infoSetMap = new HashMap(/* of InfoSet */);
// Views data indexed by ID
private Map viewNodeMap = new HashMap(/* of <view> Node */);
// Actions data indexed by view ID
private Map actionNodeMap = new HashMap(/* of Vector of <action> Node */);
// Solo (default) actions data indexed by view ID
private Map soloActionNodeMap = new HashMap(/* of Vector of <action> Node */);
// Keep track of plugins with inserted topics
private Set integratedPlugins = new HashSet(/* of String */);
/*******************************************************/
/***** InsertAction ***********************************/
/*******************************************************/
// Action command to execute the insert scripts
class InsertAction //extends HelpContribution
{
private InfoView view;
private Insert insertNode;
public InsertAction(InfoView view, Insert insertNode) {
//super(node);
this.view = view;
HelpTopicFactory.setView(this.view);
this.insertNode = insertNode;
}
/**
* Execute the insert action. May need to execute nested action if not successful.
*/
public boolean execute() {
String fromID = insertNode.getSource();
String toID = insertNode.getTarget();
String asType = insertNode.getMode();
// check conditions
if (isKnownView(fromID))
return false;
if ((isKnownTopic(fromID) || isKnownTopicSet(fromID))
&& (isKnownView(toID) || isKnownTopic(toID))) {
int pref = HelpContribution.NORMAL;
if (asType.equals(ActionContributor.INSERT_AS_FIRST_CHILD)) {
pref = HelpContribution.FIRST;
} else
if (asType.equals(ActionContributor.INSERT_AS_LAST_CHILD)) {
pref = HelpContribution.LAST;
} else
if (asType.equals(ActionContributor.INSERT_AS_PREV_SIB)) {
pref = HelpContribution.PREV;
} else
if (asType.equals(ActionContributor.INSERT_AS_NEXT_SIB)) {
pref = HelpContribution.NEXT;
}
if (insertTopic(fromID, toID, view, pref))
return true;
}
return executeNested();
}
private boolean executeNested() {
// execute each action in part
for (Iterator nested = insertNode.getChildren(); nested.hasNext();) {
InsertAction action = new InsertAction(view, (HelpInsert) nested.next());
return action.execute();
}
return false;
}
}
/**
* HelpViewManager constructor comment.
*/
public InfosetBuilder(ContributionManager cmgr) {
infosets = cmgr.getContributionsOfType(ViewContributor.INFOSET_ELEM);
topics = cmgr.getContributionsOfType(TopicContributor.TOPICS_ELEM);
actions = cmgr.getContributionsOfType(ActionContributor.ACTIONS_ELEM);
}
/**
* Builds the views objects from the view data
*/
public Map buildInformationSets() {
// Index actions and topics by ID for easier lookup
sortTopics();
sortActions();
// Create the views set objects
while (infosets.hasNext()) {
InfoSet infoset = (InfoSet) infosets.next();
infoSetMap.put(infoset.getID(), infoset);
// build the views for this info set
buildViews(infoset);
}
// run the solo actions to build stand-alone navigation
buildStandaloneNavigation();
// do not keep empty infosets that were tagged as standalone
discardEmptyStandaloneInfosets();
return infoSetMap;
}
/**
* Builds the topics contributed by standalone actions
*/
private void buildStandaloneNavigation() {
Iterator infosets = infoSetMap.values().iterator();
while (infosets.hasNext()) {
InfoSet infoset = (InfoSet) infosets.next();
InfoView[] infoviews = infoset.getViews();
for (int i = 0; i < infoviews.length; i++) {
// get list of solo actions for this view
List actions = (List) soloActionNodeMap.get(infoviews[i].getID());
List validSoloActions = getValidSoloActions(actions);
executeActions(infoviews[i], validSoloActions);
}
}
}
/**
* Builds the view object by wiring up the topics
* as specified in the insert actions.
* @return
* @param viewName java.lang.String
*/
private void buildView(InfoView view) {
String viewID = view.getID();
// Registers the view as a topic, so <insert from=topic to=view> work
((HelpInfoView) view).registerTopic(view);
topicNodeMap.put(viewID, view);
// Get build actions for this view
List actions = (List) actionNodeMap.get(viewID);
executeActions(view, actions);
}
/**
* Builds the views objects for an info set
* @return
* @param viewName java.lang.String
*/
private void buildViews(InfoSet infoSet) {
for (Iterator views = infoSet.getChildren(); views.hasNext();) {
InfoView view = (InfoView) views.next();
// build the rest of the view (wire up all the topics)
buildView(view);
}
}
/**
* Remove empty standalone infosets
*/
private void discardEmptyStandaloneInfosets() {
Iterator infosets = infoSetMap.values().iterator();
while (infosets.hasNext()) {
InfoSet infoset = (InfoSet) infosets.next();
if (!infoset.isStandalone())
continue;
InfoView[] infoviews = infoset.getViews();
boolean foundNonEmptyView = false;
for (int i = 0; !foundNonEmptyView && i < infoviews.length; i++)
if (!infoviews[i].getChildrenList().isEmpty())
foundNonEmptyView = true;
if (!foundNonEmptyView)
infosets.remove();
}
}
/**
* Builds the view object by wiring up the topics
* as specified in the insert actions.
* @return
* @param viewName java.lang.String
*/
private void executeActions(InfoView view, List actions) {
if (actions == null)
return;
List unexecutedActions = new ArrayList();
for (Iterator e = actions.iterator(); e.hasNext();) {
Action actionsNode = (Action) e.next();
// execute each action in part
for (Iterator actionsList = actionsNode.getChildren();
actionsList.hasNext();
) {
Insert insertNode = (Insert) actionsList.next();
InsertAction action = new InsertAction(view, insertNode);
if (!action.execute())
unexecutedActions.add(action);
}
}
// executing delayed actions
int noDelayed = 0;
while (unexecutedActions.size() != noDelayed) {
noDelayed = unexecutedActions.size();
for (int i = 0; i < unexecutedActions.size(); i++) {
InsertAction action = (InsertAction) unexecutedActions.get(i);
if (action.execute())
unexecutedActions.remove(i--);
}
}
}
/**
* Returns the actions are valid as solo actions.
* Valid actions are those whose "from" topics are from non-integrated plugins.
*/
private List getValidSoloActions(List actions) {
if (actions == null)
return null;
List validSoloActions = new ArrayList(actions.size());
for (Iterator e = actions.iterator(); e.hasNext();) {
Action actionsNode = (Action) e.next();
// check each action in part
boolean isSoloAction = true;
for (Iterator actionsList = actionsNode.getChildren();
isSoloAction && actionsList.hasNext();
) {
Insert insertNode = (Insert) actionsList.next();
String from = insertNode.getSource();
int index = from.lastIndexOf('.');
if (index == -1)
continue;
String plugin = from.substring(0, index);
if (integratedPlugins.contains(plugin))
isSoloAction = false;
}
if (isSoloAction)
validSoloActions.add(actionsNode);
}
return validSoloActions;
}
/**
* Inserts the topic and creates the HelpTopic objects
*/
private boolean insertTopic(
String fromTopic,
String toTopic,
InfoView view,
int positionPreference) {
if ((positionPreference == HelpContribution.PREV)
|| (positionPreference == HelpContribution.NEXT))
return insertTopicAsSib(fromTopic, toTopic, view, positionPreference);
else
return insertTopicAsChild(fromTopic, toTopic, view, positionPreference);
}
/**
* Inserts the topic and creates the HelpTopic objects
*/
private boolean insertTopicAsChild(
String fromTopic,
String toTopic,
InfoView view,
int positionPreference) {
// do a simple insert
if (isKnownTopic(fromTopic)) {
Contribution parent = ((HelpInfoView) view).getContribution(toTopic);
if (parent == null)
parent = HelpTopicFactory.createTopic((Topic) topicNodeMap.get(toTopic));
Contribution child = ((HelpInfoView) view).getContribution(fromTopic);
if (child == null)
child = HelpTopicFactory.createTopic((Topic) topicNodeMap.get(fromTopic));
((HelpContribution) parent).insertChild(child, positionPreference);
// keep track of this insertion for handling solo actions
trackTopic(fromTopic);
// now recursively insert all the children
Contribution topicNode = child;
if (child instanceof TopicRef)
topicNode = ((TopicRef) child).getTopic();
for (Iterator childTopics = topicNode.getChildren(); childTopics.hasNext();) {
Contribution childNode = (Contribution) childTopics.next();
String newFromTopic = childNode.getID();
insertTopic(newFromTopic, fromTopic, view, HelpContribution.NORMAL);
}
} else
if (isKnownTopicSet(fromTopic)) {
// insert all the child topics nodes
Contribution topicSet = (Contribution) topicSetNodeMap.get(fromTopic);
for (Iterator topics = topicSet.getChildren(); topics.hasNext();) {
Contribution topic = (Contribution) topics.next();
insertTopic(topic.getID(), toTopic, view, positionPreference);
}
}
return true; //success
}
/**
* Inserts the topic and creates the HelpTopic objects
*/
private boolean insertTopicAsSib(
String fromTopic,
String nearTopic,
InfoView view,
int positionPreference) {
// do a simple insert
if (isKnownTopic(fromTopic)) {
Contribution refSib = ((HelpInfoView) view).getContribution(nearTopic);
if (refSib == null)
return false; //sibling must already be inserted
Contribution parent = refSib.getParent();
if (parent == null)
return false; //parent must exist for proper insertion
Contribution newSib = ((HelpInfoView) view).getContribution(fromTopic);
if (newSib == null)
newSib = HelpTopicFactory.createTopic((Topic) topicNodeMap.get(fromTopic));
((HelpContribution) parent).insertNeighbouringChild(
refSib,
newSib,
positionPreference);
// keep track of this insertion for handling solo actions
trackTopic(fromTopic);
// now recursively insert all the children
Contribution topicNode = newSib;
if (newSib instanceof TopicRef)
topicNode = ((TopicRef) newSib).getTopic();
for (Iterator childTopics = topicNode.getChildren(); childTopics.hasNext();) {
Contribution childNode = (Contribution) childTopics.next();
String newFromTopic = childNode.getID();
insertTopic(newFromTopic, fromTopic, view, HelpContribution.NORMAL);
}
} else
if (isKnownTopicSet(fromTopic)) {
Contribution refSib = ((HelpInfoView) view).getContribution(nearTopic);
if (refSib == null)
return false; //sibling must exist
Contribution parent = refSib.getParent();
if (parent == null)
return false; //parent must exist for proper insertion
// insert all the child topics nodes
Contribution topicSet = (Contribution) topicSetNodeMap.get(fromTopic);
List topics = topicSet.getChildrenList();
if (positionPreference == HelpContribution.NEXT) {
for (int i = topics.size() - 1; i >= 0; i--) {
Contribution topic = (Contribution) topics.get(i);
insertTopic(topic.getID(), nearTopic, view, positionPreference);
}
} else {
for (int i = 0; i < topics.size(); i++) {
Contribution topic = (Contribution) topics.get(i);
insertTopic(topic.getID(), nearTopic, view, positionPreference);
}
}
}
return true; //success
}
public boolean isKnownTopic(String topicID) {
return topicNodeMap.get(topicID) != null;
}
public boolean isKnownTopicSet(String topicSetID) {
return topicSetNodeMap.get(topicSetID) != null;
}
private boolean isKnownView(String viewName) {
return (viewNodeMap.get(viewName) != null);
}
/**
* Indexes actions by view id
* @return java.util.Vector
* @param viewName java.lang.String
*/
private void sortActions() {
while (actions.hasNext()) {
Action actionNode = (Action) actions.next();
String viewID = actionNode.getView();
if (viewID.equals("")) {
// XXX EXCEPTION!!!
} else {
// do a CASE SENSITIVE match on the view name.
// We store a vector of actions under one id
if (actionNode.isStandalone()) {
List actions = (List) soloActionNodeMap.get(viewID);
if (actions == null)
actions = new ArrayList();
actions.add(actionNode);
soloActionNodeMap.put(viewID, actions);
} else {
List actions = (List) actionNodeMap.get(viewID);
if (actions == null)
actions = new ArrayList();
actions.add(actionNode);
actionNodeMap.put(viewID, actions);
}
}
}
}
/**
* Indexes topics by their ID for easier lookup
*/
private void sortTopics() {
while (topics.hasNext()) {
Contribution topicSet = (Contribution) topics.next();
topicSetNodeMap.put(topicSet.getID(), topicSet);
// Put all the topics (the whole nested tree) into a hashtable indexed by id
Stack stack = new Stack();
stack.push(topicSet.getChildren());
while (!stack.isEmpty()) {
Iterator children = (Iterator) stack.pop();
while (children.hasNext()) {
Contribution topic = (Contribution) children.next();
topicNodeMap.put(topic.getID(), topic);
Iterator subtopics = topic.getChildren();
if (subtopics.hasNext())
stack.push(subtopics);
}
}
}
}
/**
* Tracks the topics by its plugin.
* All the integrated plugins will be remembered, so the solo actions
* will not be applied to them.
*/
private void trackTopic(String topicId) {
int i = topicId.lastIndexOf('.');
if (i == -1)
return;
String pluginId = topicId.substring(0, i);
integratedPlugins.add(pluginId);
}
}