/******************************************************************************* * Copyright (c) 2009, 2013 David Green 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: * David Green - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.internal.wikitext.context.ui; import java.io.BufferedInputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.content.IContentDescription; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.mylyn.context.core.AbstractContextStructureBridge; import org.eclipse.mylyn.context.core.ContextCore; import org.eclipse.mylyn.wikitext.parser.markup.MarkupLanguage; import org.eclipse.mylyn.wikitext.parser.outline.OutlineItem; import org.eclipse.mylyn.wikitext.parser.outline.OutlineParser; import org.eclipse.mylyn.wikitext.ui.WikiText; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.PlatformUI; /** * A Mylyn {@link AbstractContextStructureBridge context structure bridge} for wikitext files. * * @author David Green */ public class WikiTextContextStructureBridge extends AbstractContextStructureBridge { private static final char HANDLE_FILE_SEPARATOR = ';'; public static final String CONTENT_TYPE = WikiText.CONTENT_TYPE; @Override public boolean acceptsObject(Object object) { if (object instanceof OutlineItem) { return true; } else if (object instanceof IFile) { IFile file = (IFile) object; try { IContentDescription description = file.getContentDescription(); if (description != null) { IContentType contentType = description.getContentType(); if (contentType != null) { if (isWikiText(contentType)) { return true; } } } } catch (CoreException e) { // ignore } String languageName = WikiText.getMarkupLanguageNameForFilename(file.getName()); return languageName != null; } return false; } private boolean isWikiText(IContentType contentType) { if (WikiText.CONTENT_TYPE.equals(contentType.getId())) { return true; } IContentType baseType = contentType.getBaseType(); if (baseType != null) { return isWikiText(baseType); } return false; } @Override public boolean canBeLandmark(String handle) { return handle != null; } @Override public boolean canFilter(Object element) { return true; } @Override public List<String> getChildHandles(String handle) { Object object = getObjectForHandle(handle); if (object instanceof OutlineItem) { OutlineItem item = (OutlineItem) object; if (!item.getChildren().isEmpty()) { List<String> handles = new ArrayList<String>(item.getChildren().size()); for (OutlineItem child : item.getChildren()) { handles.add(getHandleIdentifier(child)); } return handles; } } return Collections.emptyList(); } @Override public String getContentType() { return CONTENT_TYPE; } @Override public String getContentType(String elementHandle) { if (elementHandle.indexOf(HANDLE_FILE_SEPARATOR) == -1) { return parentContentType; } return CONTENT_TYPE; } @Override public String getHandleForOffsetInObject(Object object, int offset) { IResource resource = null; try { if (object instanceof IResource) { resource = (IResource) object; } else if (object instanceof IMarker) { resource = ((IMarker) object).getResource(); } else { try { // works with ConcreteMarker without creating a compile-time dependency on internals IMarker marker = (IMarker) object.getClass().getMethod("getMarker").invoke(object); //$NON-NLS-1$ resource = marker.getResource(); } catch (Exception e) { // ignore } } } catch (Exception e) { return null; } if (resource instanceof IFile) { IFile file = (IFile) resource; if (acceptsObject(file)) { OutlineItem outline = getOutline(file); if (outline != null) { OutlineItem item = outline.findNearestMatchingOffset(offset); if (item != null) { return getHandleIdentifier(item); } } } } return null; } @Override public String getLabel(Object object) { if (object instanceof OutlineItem) { OutlineItem item = (OutlineItem) object; if (item.getParent() == null) { return getFile(item).getName(); } else { return item.getLabel(); } } return ""; //$NON-NLS-1$ } @Override public Object getObjectForHandle(String handle) { if (handle == null) { return null; } int idxOfSeparator = handle.indexOf(HANDLE_FILE_SEPARATOR); String filename = handle; if (idxOfSeparator != -1) { filename = handle.substring(0, idxOfSeparator); } IFile file; try { file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(filename)); } catch (Exception e) { // be error-tolerant since we don't know much about the handle at this point return null; } if (file != null) { if (idxOfSeparator != -1) { String headingId = handle.substring(idxOfSeparator + 1); OutlineItem outline = getOutline(file); if (outline != null) { OutlineItem item = outline.findItemById(headingId); return item; } } else { return file; } } return null; } @Override public String getParentHandle(String handle) { Object object = getObjectForHandle(handle); if (object instanceof OutlineItem) { OutlineItem item = (OutlineItem) object; if (item.getParent() != null) { return getHandleIdentifier(item.getParent()); } else { return getHandleIdentifier(getFile(item)); } } else if (object instanceof IFile) { AbstractContextStructureBridge parentBridge = ContextCore.getStructureBridge(parentContentType); return parentBridge.getParentHandle(handle); } return null; } @Override public boolean isDocument(String handle) { Object object = getObjectForHandle(handle); if (object instanceof OutlineItem) { OutlineItem item = (OutlineItem) object; return item.getParent() == null; } return false; } @Override public String getHandleIdentifier(Object object) { if (object instanceof OutlineItem) { OutlineItem item = (OutlineItem) object; return item.getResourcePath() + HANDLE_FILE_SEPARATOR + item.getId(); } else if (object instanceof IFile && acceptsObject(object)) { return ((IFile) object).getFullPath().toString(); } return null; } private IPath getFullPath(OutlineItem item) { String resourcePath = item.getResourcePath(); return resourcePath == null ? null : new Path(resourcePath); } private OutlineItem getOutline(IFile file) { // FIXME: is editor integration the way to go?? we probably need some kind of core model IEditorPart editorPart = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor(); if (editorPart != null) { OutlineItem outline = (OutlineItem) editorPart.getAdapter(OutlineItem.class); if (outline != null) { return outline; } } MarkupLanguage markupLanguage = WikiText.getMarkupLanguageForFilename(file.getName()); if (markupLanguage != null) { OutlineParser parser = new OutlineParser(markupLanguage); try { String contents = getContents(file); OutlineItem outline = parser.parse(contents); outline.setResourcePath(file.getFullPath().toString()); return outline; } catch (Exception e) { // ignore return null; } } return null; } private String getContents(IFile file) throws Exception { String charset = file.getCharset(); StringWriter writer = new StringWriter(); Reader reader = new InputStreamReader(new BufferedInputStream(file.getContents()), charset); int i; while ((i = reader.read()) != -1) { writer.write(i); } return writer.toString(); } IFile getFile(OutlineItem item) { IPath fullPath = getFullPath(item); return fullPath == null ? null : ResourcesPlugin.getWorkspace().getRoot().getFile(fullPath); } }