/*******************************************************************************
* 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);
}
}