/******************************************************************************* | |
* Copyright (c) 2011 IBM Corporation 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: | |
* IBM Corporation - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.wst.css.ui.internal.hyperlink; | |
import java.io.File; | |
import java.net.URI; | |
import org.eclipse.core.resources.IFile; | |
import org.eclipse.core.resources.ResourcesPlugin; | |
import org.eclipse.core.runtime.IPath; | |
import org.eclipse.core.runtime.Path; | |
import org.eclipse.jface.text.IDocument; | |
import org.eclipse.jface.text.IRegion; | |
import org.eclipse.jface.text.ITextViewer; | |
import org.eclipse.jface.text.Region; | |
import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; | |
import org.eclipse.jface.text.hyperlink.IHyperlink; | |
import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolverPlugin; | |
import org.eclipse.wst.css.core.internal.document.CSSRegionContainer; | |
import org.eclipse.wst.css.core.internal.provisional.document.ICSSNode; | |
import org.eclipse.wst.sse.core.StructuredModelManager; | |
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; | |
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; | |
import org.w3c.dom.css.CSSImportRule; | |
import org.w3c.dom.css.CSSPrimitiveValue; | |
/** | |
* Detects hyperlink regions within CSS documents. This includes url() methods as | |
* well as the resource referred to by @import | |
* | |
*/ | |
public class CSSHyperlinkDetector extends AbstractHyperlinkDetector { | |
/* (non-Javadoc) | |
* @see org.eclipse.jface.text.hyperlink.IHyperlinkDetector#detectHyperlinks(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion, boolean) | |
*/ | |
public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { | |
if (textViewer != null && region != null) { | |
final IDocument document = textViewer.getDocument(); | |
final ICSSNode node = getNode(document, region); | |
if (node == null) { | |
return null; | |
} | |
String href = null; | |
switch (node.getNodeType()) { | |
case ICSSNode.PRIMITIVEVALUE_NODE: | |
if (((CSSPrimitiveValue)node).getPrimitiveType() == CSSPrimitiveValue.CSS_URI) { | |
href = ((CSSPrimitiveValue) node).getStringValue(); | |
} | |
break; | |
case ICSSNode.IMPORTRULE_NODE: | |
href = ((CSSImportRule) node).getHref(); | |
break; | |
} | |
if (href != null) { | |
final IHyperlink hyperlink = getHyperlink(node, href); | |
if (hyperlink != null) { | |
return new IHyperlink[] { hyperlink }; | |
} | |
} | |
} | |
return null; | |
} | |
private ICSSNode getNode(IDocument document, IRegion region) { | |
if (!(document instanceof IStructuredDocument)) | |
return null; | |
IStructuredModel model = null; | |
ICSSNode node = null; | |
try { | |
model = StructuredModelManager.getModelManager().getModelForRead((IStructuredDocument)document); | |
node = (ICSSNode) model.getIndexedRegion(region.getOffset()); | |
} | |
finally { | |
if (model != null) { | |
model.releaseFromRead(); | |
} | |
} | |
return node; | |
} | |
private IHyperlink getHyperlink(ICSSNode node, String href) { | |
IHyperlink hyperlink = null; | |
final String baseLocation = getBaseLocation(node.getOwnerDocument().getModel()); | |
if (baseLocation != null) { | |
final String resolvedHref = URIResolverPlugin.createResolver().resolve(baseLocation, null, href); | |
if (resolvedHref != null && isValidURI(resolvedHref)) { | |
final IRegion hyperlinkRegion = getHyperlinkRegion(node, href); | |
hyperlink = createHyperlink(resolvedHref, hyperlinkRegion); | |
} | |
} | |
return hyperlink; | |
} | |
private IRegion getHyperlinkRegion(ICSSNode node, String href) { | |
CSSRegionContainer uriRegion = null; | |
switch (node.getNodeType()) { | |
case ICSSNode.PRIMITIVEVALUE_NODE: | |
uriRegion = (CSSRegionContainer) node; | |
break; | |
case ICSSNode.IMPORTRULE_NODE: | |
ICSSNode attribute = node.getAttributes().getNamedItem("href"); //$NON-NLS-1$ | |
if (attribute instanceof CSSRegionContainer) { | |
uriRegion = (CSSRegionContainer) attribute; | |
} | |
break; | |
} | |
if (uriRegion != null) { | |
final int start = uriRegion.getStartOffset(); | |
final int end = uriRegion.getEndOffset(); | |
if (end > start) | |
return new Region(start, end - start); | |
} | |
return null; | |
} | |
private IHyperlink createHyperlink(String href, IRegion region) { | |
IHyperlink link = null; | |
// try to locate the file in the workspace | |
File systemFile = getFileFromUriString(href); | |
if (systemFile != null) { | |
String systemPath = systemFile.getPath(); | |
IFile file = getFile(systemPath); | |
if (file != null) { | |
// this is a WorkspaceFileHyperlink since file exists in | |
// workspace | |
link = new WorkspaceFileHyperlink(region, file); | |
} | |
} | |
return link; | |
} | |
/** | |
* Returns an IFile from the given uri if possible, null if cannot find | |
* file from uri. | |
* | |
* @param fileString | |
* file system path | |
* @return returns IFile if fileString exists in the workspace | |
*/ | |
private IFile getFile(String fileString) { | |
IFile file = null; | |
if (fileString != null) { | |
Path filePath = new Path(fileString); | |
if (filePath.segmentCount() > 1 && ResourcesPlugin.getWorkspace().getRoot().getFile(filePath).exists()) { | |
return ResourcesPlugin.getWorkspace().getRoot().getFile(filePath); | |
} | |
IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocation(filePath); | |
for (int i = 0; (i < files.length) && (file == null); i++) { | |
if (files[i].exists()) { | |
file = files[i]; | |
} | |
} | |
} | |
return file; | |
} | |
/** | |
* Checks whether the given uriString is really pointing to a file | |
* | |
* @param uriString | |
* @return boolean | |
*/ | |
private boolean isValidURI(String uriString) { | |
boolean isValid = false; | |
File file = getFileFromUriString(uriString); | |
if (file != null) { | |
isValid = file.isFile(); | |
} | |
return isValid; | |
} | |
/** | |
* Create a file from the given uri string | |
* | |
* @param uriString - | |
* assumes uriString is not http:// | |
* @return File created from uriString if possible, null otherwise | |
*/ | |
private File getFileFromUriString(String uriString) { | |
File file = null; | |
try { | |
// first just try to create a file directly from uriString as | |
// default in case create file from uri does not work | |
file = new File(uriString); | |
// try to create file from uri | |
URI uri = new URI(uriString); | |
file = new File(uri); | |
} | |
catch (Exception e) { | |
// if exception is thrown while trying to create File just ignore | |
// and file will be null | |
} | |
return file; | |
} | |
/** | |
* Get the base location from the current model (local file system) | |
*/ | |
private String getBaseLocation(IStructuredModel model) { | |
String result = null; | |
// get the base location from the current model | |
if (model != null) { | |
result = model.getBaseLocation(); | |
IPath path = new Path(result); | |
if (path.segmentCount() > 1) { | |
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path); | |
if (file.exists()) { | |
String baseLocation = null; | |
if (file.getLocation() != null) { | |
baseLocation = file.getLocation().toString(); | |
} | |
if (baseLocation == null && file.getLocationURI() != null) { | |
baseLocation = file.getLocationURI().toString(); | |
} | |
if (baseLocation == null) { | |
baseLocation = file.getFullPath().toString(); | |
} | |
result = baseLocation; | |
} | |
} | |
} | |
return result; | |
} | |
} |