Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 346832

Summary: IHelpResource.getHref() does not work correctly for "file:/" URL schema
Product: [Eclipse Project] Platform Reporter: Peter Dell <Peter.Dell>
Component: User AssistanceAssignee: platform-ua-inbox <platform-ua-inbox>
Status: CLOSED WORKSFORME QA Contact:
Severity: normal    
Priority: P3 CC: cgold, prakash
Version: 4.1   
Target Milestone: ---   
Hardware: PC   
OS: Windows 7   
Whiteboard:

Description Peter Dell CLA 2011-05-23 03:38:44 EDT
Build Identifier: M20110210-1200

My implementation of org.eclipse.help return "file:/C:/jac/system/Atari2600/Tools/ASM/DASM/bin/DOS/../../doc/DASM.TXT" in "getHref()". This absolute URL is not recognized as such and used as relative path resulting in "http://127.0.0.1:53893/help/topic/file:/C:/jac/system/Atari2600/Tools/ASM/DASM/doc/DASM.TXT". The JavDoc state that absolute URLs like "http://" and "jar://file/..." do work.

Reproducible: Always

Steps to Reproduce:
1. Implement and register a ToC provider
2. Return file URLs in getHref().

Example source:

/**
 * Copyright (C) 2009 - 2011 <a href="http://www.wudsn.com" target="_top">Peter Dell</a>
 *
 * This file is part of WUDSN IDE.
 * 
 * WUDSN IDE 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
 * (at your option) any later version.
 * 
 * WUDSN IDE 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 WUDSN IDE.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.wudsn.ide.asm;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.help.AbstractTocProvider;
import org.eclipse.help.ICriteria;
import org.eclipse.help.IToc;
import org.eclipse.help.ITocContribution;
import org.eclipse.help.ITopic;
import org.eclipse.help.ITopic2;
import org.eclipse.help.IUAElement;

import com.wudsn.ide.asm.compiler.CompilerDefinition;
import com.wudsn.ide.asm.compiler.CompilerRegistry;
import com.wudsn.ide.asm.editor.HardwareUtility;
import com.wudsn.ide.asm.preferences.CompilerPreferences;
import com.wudsn.ide.base.common.EnumUtility;
import com.wudsn.ide.base.common.StringUtility;

/**
 * Dynamic help content provider. Uses static pages and the meta data from the
 * compiler definitions to build a comprehensive help.
 * 
 * @author Peter Dell
 * 
 * TODO: Make links to local help documents with "file:/" work
 * 
 * @since 1.6.1
 */
public final class AssemblerTocProvider extends AbstractTocProvider {

    private final class Toc implements IToc {
	private ITopic[] topics;

	public Toc() {

	    AssemblerPlugin assemblerPlugin = AssemblerPlugin.getInstance();
	    CompilerRegistry compilerRegistry = assemblerPlugin
		    .getCompilerRegistry();
	    Hardware[] hardwares = Hardware.values();
	    List<ITopic> hardwareTopics = new ArrayList<ITopic>(
		    hardwares.length - 1);
	    for (Hardware hardware : hardwares) {

		if (hardware == Hardware.GENERIC) {
		    continue;
		}

		List<CompilerDefinition> compilerDefinitions = compilerRegistry
			.getCompilerDefinitions(hardware);
		int size = compilerDefinitions.size();
		List<ITopic> assemblerTopics = new ArrayList<ITopic>(size);

		for (int i = 0; i < size; i++) {
		    CompilerDefinition compilerDefinition = compilerDefinitions
			    .get(i);
		    CompilerPreferences compilerPreferences = assemblerPlugin
			    .getPreferences().getCompilerPreferences(
				    compilerDefinition.getId());
		    String compilerExecutablePath = compilerPreferences
			    .getExecutablePath();
		    if (StringUtility.isSpecified(compilerExecutablePath)) {

			File file = new File(
				new File(compilerExecutablePath).getParent(),
				compilerDefinition.getHelpFilePath());

			String href = file.toURI().toString();
//			 if (href.startsWith("file:/")
//			 && !href.startsWith("file://")) {
//			 href = "http://" + href.substring("file:/".length());
//			 }
			assemblerTopics.add(new Topic("", compilerDefinition
				.getName(), href, new ITopic[0]));

		    }
		}
		if (!assemblerTopics.isEmpty()) {
		    ITopic[] assemblerTopicsArray;
		    assemblerTopicsArray = new ITopic[assemblerTopics.size()];
		    assemblerTopics.toArray(assemblerTopicsArray);
		    Topic hardwareTopic = new Topic("icons/"
			    + HardwareUtility.getImagePath(hardware),
			    EnumUtility.getText(hardware), "",
			    assemblerTopicsArray);
		    hardwareTopics.add(hardwareTopic);
		}
	    }

	    ITopic[] hardwareTopicsArray;
	    hardwareTopicsArray = new ITopic[hardwareTopics.size()];
	    hardwareTopics.toArray(hardwareTopicsArray);

	    topics = new Topic[2];
	    topics[0] = new Topic("", Texts.TOC_ASSEMBLER_TOPIC_LABEL, "",
		    hardwareTopicsArray);
	    topics[1] = new Topic("", Texts.TOC_VIDEO_TUTORIALS_LABEL,
		    "doc/video-tutorials.html", new ITopic[0]);
	}

	public boolean isEnabled(IEvaluationContext context) {
	    return true;
	}

	public IUAElement[] getChildren() {
	    return topics;
	}

	public String getHref() {
	    return "";
	}

	public String getLabel() {
	    return Texts.TOC_WUDSN_IDE_LABEL;
	}

	public ITopic[] getTopics() {
	    return topics;
	}

	public ITopic getTopic(String href) {
	    return topics[0];
	}
    }

    private final class Topic implements ITopic2 {

	private String icon;
	private String label;
	private String href;
	private ITopic[] subtopics;

	private IUAElement[] children;

	public Topic(String icon, String label, String href, ITopic[] subtopics) {
	    if (icon == null) {
		throw new IllegalArgumentException(
			"Parameter 'icon' must not be null.");
	    }
	    if (label == null) {
		throw new IllegalArgumentException(
			"Parameter 'label' must not be null.");
	    }
	    if (href == null) {
		throw new IllegalArgumentException(
			"Parameter 'href' must not be null.");
	    }
	    if (subtopics == null) {
		throw new IllegalArgumentException(
			"Parameter 'subtopics' must not be null.");
	    }
	    this.icon = icon;
	    this.label = label;
	    this.href = href;
	    this.subtopics = subtopics;
	    children = new IUAElement[subtopics.length];
	    System.arraycopy(subtopics, 0, children, 0, subtopics.length);
	}

	public boolean isEnabled(IEvaluationContext context) {
	    return true;
	}

	public String getIcon() {
	    return icon;
	}

	public String getLabel() {
	    return label;
	}

	public String getHref() {
	    return href;
	}

	public ITopic[] getSubtopics() {
	    return subtopics;
	}

	public IUAElement[] getChildren() {
	    return children;
	}

	public boolean isSorted() {
	    return true;
	}

	public ICriteria[] getCriteria() {
	    return null;
	}

    }

    private final class TocContribution implements ITocContribution {

	public TocContribution() {
	}

	public String getCategoryId() {
	    return "CategoryID";
	}

	public String getContributorId() {
	    return AssemblerPlugin.ID;
	}

	public String[] getExtraDocuments() {
	    return new String[0];
	}

	public String getId() {
	    return "ID";
	}

	public String getLocale() {
	    return "";
	}

	public String getLinkTo() {
	    return "";
	}

	public IToc getToc() {
	    return new Toc();
	}

	public boolean isPrimary() {
	    return true;
	}

    }

    public AssemblerTocProvider() {
    }

    @Override
    public ITocContribution[] getTocContributions(String locale) {
	return new ITocContribution[] { new TocContribution() };
    }

}
Comment 1 Prakash Rangaraj CLA 2011-05-23 03:45:23 EDT
(In reply to comment #0)

You should only upload EPL licensed code into Eclipse Bugzilla. You should remove this comment (only webmaster has the rights to delete the comments, so raise a bug against webmaster)
Comment 2 Peter Dell CLA 2011-05-23 10:24:27 EDT
Hello Prakash,
Thanks, but since this code is my very own creation and I am the one who is giving the non-exlcusive licences, I don't see the problem. And it is here in order to reproduce the bug.
Cheers, Peter.
Comment 3 Prakash Rangaraj CLA 2011-05-23 11:18:37 EDT
(In reply to comment #2)
> Hello Prakash,
> Thanks, but since this code is my very own creation and I am the one who is
> giving the non-exlcusive licences, I don't see the problem. And it is here in
> order to reproduce the bug.

   It doesn't matter who created it. Its been uploaded with a GPL license.
Comment 4 Chris Goldthorpe CLA 2011-05-23 13:16:02 EDT
Do you have a standalone example which illustrates the problem? I believe that the problem is not the use of as TocProvider but is instead a restriction on the use of the file: protocol for security reasons and you would see the same issue if you created a toc.xml file where the topic hrefs used the file: protocol. The workaround is to uses a content provider rather than use the file: protocol.
Comment 5 Peter Dell CLA 2011-05-23 17:19:17 EDT
Thanks Chris, this is excatly the information I was looking for also for another use case where the help content is created completly on a dynamic basis! I 

Best regards, Peter.