| /******************************************************************************* |
| * Copyright (c) 2004, 2007 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.html.core.internal.htmlcss; |
| |
| |
| |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.UnsupportedEncodingException; |
| import java.nio.charset.UnsupportedCharsetException; |
| |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| 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.wst.html.core.internal.document.HTMLDocumentTypeConstants; |
| import org.eclipse.wst.sse.core.StructuredModelManager; |
| import org.eclipse.wst.sse.core.internal.encoding.EncodingRule; |
| import org.eclipse.wst.sse.core.internal.provisional.IModelManager; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceAlreadyExists; |
| import org.eclipse.wst.sse.core.internal.provisional.exceptions.ResourceInUse; |
| import org.eclipse.wst.sse.core.internal.util.PathHelper; |
| import org.eclipse.wst.sse.core.internal.util.ProjectResolver; |
| import org.eclipse.wst.sse.core.internal.util.URIResolver; |
| import org.eclipse.wst.xml.core.internal.document.DocumentTypeAdapter; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| // TODO when this class is removed from .core, PathHelper and URLHelper class |
| // also can be removed. |
| |
| /** |
| */ |
| public class URLModelProvider { |
| |
| private static final int GET_MODEL_FOR_READ = 1; |
| // private static final int GET_NEW_MODEL_FOR_READ = 2; |
| private static final int GET_MODEL_FOR_EDIT = 3; |
| // private static final int GET_NEW_MODEL_FOR_EDIT = 4; |
| // private static final int READ_BUFFER_SIZE = 4096; |
| // IModelManager |
| private IModelManager modelManager = null; |
| |
| /** |
| */ |
| public URLModelProvider() { |
| super(); |
| |
| // obtain model manager |
| modelManager = StructuredModelManager.getModelManager(); |
| } |
| |
| /** |
| * Calculate ID from a filename. This must be same as |
| * FileModelProvider.calculateId(IFile) |
| */ |
| private static String calculateId(IPath fullIPath) { |
| return fullIPath.toString(); |
| } |
| |
| /** |
| * <code>baseModel</code>: the model containing the link |
| * <code>ref</code>: the link URL string |
| */ |
| private IStructuredModel getCommonModelFor(final IStructuredModel baseModel, final String ref, final int which) throws IOException { |
| // first, create absolute url |
| String absURL = resolveURI(baseModel, ref, true); |
| if ((absURL == null) || (absURL.length() == 0)) { |
| return null; |
| } |
| |
| // need to remove file:// scheme if necessary |
| try { |
| final java.net.URL aURL = new java.net.URL(absURL); |
| // An actual URL was given, only file:/// is supported |
| // resolve it by finding the file it points to |
| if (!aURL.getProtocol().equals("platform")) { //$NON-NLS-1$ |
| if (aURL.getProtocol().equals("file") && (aURL.getHost().equals("localhost") || aURL.getHost().length() == 0)) {//$NON-NLS-2$//$NON-NLS-1$ |
| absURL = aURL.getFile(); |
| final IPath ipath = new Path(absURL); |
| // if path has a device, and if it begins with |
| // IPath.SEPARATOR, remove it |
| final String device = ipath.getDevice(); |
| if ((device != null) && (device.length() > 0)) { |
| if (device.charAt(0) == IPath.SEPARATOR) { |
| final String newDevice = device.substring(1); |
| absURL = ipath.setDevice(newDevice).toString(); |
| } |
| } |
| |
| } |
| } |
| } |
| catch (java.net.MalformedURLException mfuExc) { |
| } |
| |
| |
| // next, decide project |
| IProject project = null; |
| final IPath fullIPath = new Path(absURL); |
| IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot(); |
| IContainer container = workspace.getContainerForLocation(fullIPath); |
| if (container != null) { |
| // fullIPath doesn't exist in workspace |
| project = container.getProject(); |
| } |
| |
| // If HTML document has a link to an extern CSS which is not in |
| // IProject |
| // workspace.getContainerForLoation() may return null. We need to take |
| // care |
| // of this case |
| |
| // now, get absURL's IFile |
| if ((project != null) && (project.getLocation().isPrefixOf(fullIPath) == false)) { |
| // it's at outside of Project |
| return null; |
| } |
| |
| IStructuredModel model = null; |
| if (project != null) { |
| IPath filePath = fullIPath.removeFirstSegments(project.getLocation().segmentCount()); |
| IFile file = (filePath != null && !filePath.isEmpty()) ? project.getFile(filePath) : null; |
| if (file == null) { |
| return null; |
| } |
| |
| // obtain model |
| if (which == GET_MODEL_FOR_EDIT) { |
| model = getModelForEdit(file); |
| } |
| else if (which == GET_MODEL_FOR_READ) { |
| model = getModelForRead(file); |
| } |
| |
| // setting synchronization stamp is IModelManager's client's |
| // responsibility |
| if (model != null && model.getSynchronizationStamp() == IResource.NULL_STAMP) |
| model.resetSynchronizationStamp(file); |
| } |
| else { |
| String id = null; |
| InputStream inStream = null; |
| // obtain resolver |
| URIResolver resolver = (project != null) ? (URIResolver) project.getAdapter(URIResolver.class) : null; |
| if (resolver == null) { |
| // ProjectResolver can take care of the case if project is |
| // null. |
| resolver = new ProjectResolver(project); |
| } |
| if (resolver == null) { |
| return null; |
| } |
| |
| // there is no project. we can't expect IProject help to create |
| // id/inputStream |
| java.io.File file = fullIPath.toFile(); |
| |
| // obatin id |
| id = calculateId(fullIPath); |
| |
| // obtain InputStream |
| try { |
| inStream = new FileInputStream(file); |
| } |
| catch (FileNotFoundException fnfe) { |
| // the file does not exist, or we don't have read permission |
| return null; |
| } |
| |
| // obtain model |
| try { |
| if (which == GET_MODEL_FOR_EDIT) { |
| model = getModelManager().getModelForEdit(id, inStream, resolver); |
| } |
| else if (which == GET_MODEL_FOR_READ) { |
| model = getModelManager().getModelForRead(id, inStream, resolver); |
| } |
| } |
| catch (UnsupportedEncodingException ue) { |
| } |
| catch (IOException ioe) { |
| } |
| finally { |
| // close now ! |
| if (inStream != null) { |
| inStream.close(); |
| } |
| } |
| } |
| |
| |
| // set locationid |
| if (model != null && model.getBaseLocation() == null) { |
| model.setBaseLocation(fullIPath.toString()); |
| } |
| |
| return model; |
| } |
| |
| /** |
| * <code>baseModel</code>: the model containing the link |
| * <code>ref</code>: the link URL string |
| */ |
| public IStructuredModel getModelForEdit(IStructuredModel baseModel, String ref) throws IOException { |
| return getCommonModelFor(baseModel, ref, GET_MODEL_FOR_EDIT); |
| } |
| |
| /** |
| */ |
| private IStructuredModel getModelForEdit(IFile file) throws IOException { |
| if (file == null) |
| return null; |
| IModelManager manager = getModelManager(); |
| |
| // create a fake InputStream |
| IStructuredModel model = null; |
| try { |
| model = manager.getModelForEdit(file); |
| } |
| catch (UnsupportedCharsetException ex) { |
| try { |
| model = manager.getModelForEdit(file, EncodingRule.FORCE_DEFAULT); |
| } |
| catch (IOException ioe) { |
| } |
| catch (CoreException ce) { |
| } |
| } |
| catch (CoreException ce) { |
| } |
| return model; |
| } |
| |
| /** |
| * <code>baseModel</code>: the model containing the link |
| * <code>ref</code>: the link URL string |
| */ |
| public IStructuredModel getModelForRead(IStructuredModel baseModel, String ref) throws UnsupportedEncodingException, IOException { |
| return getCommonModelFor(baseModel, ref, GET_MODEL_FOR_READ); |
| } |
| |
| /** |
| */ |
| private IStructuredModel getModelForRead(IFile file) throws IOException { |
| if (file == null) |
| return null; |
| IModelManager manager = getModelManager(); |
| |
| // create a fake InputStream |
| IStructuredModel model = null; |
| try { |
| model = manager.getModelForRead(file); |
| } |
| catch (UnsupportedCharsetException ex) { |
| try { |
| model = manager.getModelForRead(file, EncodingRule.FORCE_DEFAULT); |
| } |
| catch (IOException ioe) { |
| } |
| catch (CoreException ce) { |
| } |
| } |
| catch (CoreException ce) { |
| } |
| return model; |
| } |
| |
| /** |
| */ |
| private IModelManager getModelManager() { |
| return modelManager; |
| } |
| |
| public IStructuredModel getNewModelForEdit(IFile iFile) { |
| if (iFile == null) |
| return null; |
| IModelManager manager = getModelManager(); |
| if (manager == null) |
| return null; |
| |
| IStructuredModel model = null; |
| try { |
| model = manager.getNewModelForEdit(iFile, false); |
| } |
| catch (IOException ex) { |
| } |
| catch (ResourceInUse riu) { |
| } |
| catch (ResourceAlreadyExists rae) { |
| } |
| catch (CoreException ce) { |
| } |
| return model; |
| } |
| |
| public IStructuredModel getNewModelForRead(IFile iFile) { |
| if (iFile == null) |
| return null; |
| IModelManager manager = getModelManager(); |
| if (manager == null) |
| return null; |
| |
| IStructuredModel model = null; |
| try { |
| model = manager.getNewModelForEdit(iFile, false); |
| } |
| catch (IOException ex) { |
| } |
| catch (ResourceInUse riu) { |
| } |
| catch (ResourceAlreadyExists rae) { |
| } |
| catch (CoreException ce) { |
| } |
| return model; |
| } |
| |
| /** |
| * Utility to check the model is HTML family or not |
| */ |
| static private boolean isHTMLFamily(IStructuredModel model) { |
| if (model instanceof IDOMModel) { |
| IDOMDocument document = ((IDOMModel) model).getDocument(); |
| DocumentTypeAdapter adapter = (DocumentTypeAdapter) document.getAdapterFor(DocumentTypeAdapter.class); |
| if (adapter != null) |
| return adapter.hasFeature(HTMLDocumentTypeConstants.HTML); |
| } |
| return false; |
| } |
| |
| /** |
| * <code>baseModel</code>: the model containing the link |
| * <code>ref</code>: the link URL string |
| * <code>resolveCrossProjectLinks</code>: If resolveCrossProjectLinks |
| * is set to true, then this method will properly resolve the URI if it is |
| * a valid URI pointing to another (appropriate) project. |
| */ |
| public static String resolveURI(IStructuredModel baseModel, String ref, boolean resolveCrossProjectLinks) { |
| if (baseModel == null) |
| return null; |
| // for HTML, 'href' attribute value of BASE element |
| // should be used, if exists any |
| String baseHref = null; |
| // dmw_TODO needs to be changed to handle a content model |
| // of HTML or XHTML |
| if (isHTMLFamily(baseModel)) { |
| final IDOMModel xmlmodel = (IDOMModel) baseModel; |
| final IDOMDocument doc = xmlmodel.getDocument(); |
| // look for <BASE> w/ href |
| final NodeList nl = doc.getElementsByTagName("BASE");//$NON-NLS-1$ |
| if ((nl != null) && (nl.getLength() > 0)) { |
| // per each <BASE> |
| for (int i = 0; i < nl.getLength(); i++) { |
| final Node baseNode = nl.item(i); |
| if (baseNode != null) { |
| // get all attrs |
| final NamedNodeMap attrNodes = baseNode.getAttributes(); |
| if (attrNodes != null) { |
| final Node attrNode = attrNodes.getNamedItem("HREF");//$NON-NLS-1$ |
| if (attrNode != null) { |
| // found href="" |
| final String attrValue = attrNode.getNodeValue(); |
| if (attrValue != null) { |
| baseHref = attrValue.trim(); |
| } |
| } |
| } |
| } |
| // what if there are multiple <BASE> tags ?? |
| if (baseHref != null) { |
| break; |
| } |
| } |
| } |
| } |
| |
| // get resolver in Model |
| final URIResolver resolver = baseModel.getResolver(); |
| |
| // resolve to absolute url |
| final String absurl = (resolver != null) ? ((baseHref != null) ? resolver.getLocationByURI(ref, baseHref, resolveCrossProjectLinks) : resolver.getLocationByURI(ref, resolveCrossProjectLinks)) : null; |
| if ((resolver != null) && (absurl == null) && (ref != null) && (ref.trim().length() > 0) && (ref.trim().charAt(0) == '/')) { |
| // to reach here means : |
| // ref is a Docroot relative |
| // resolver can't resolve ref |
| // so that href is a broken and should not create model |
| return null; |
| } |
| if ((absurl != null) && (absurl.length() > 0)) { |
| return absurl; |
| } |
| |
| // maybe ref is at outside of the Project |
| // obtain docroot; |
| final IContainer container = (resolver != null) ? resolver.getRootLocation() : null; |
| String docroot = null; |
| if (container != null) { |
| IPath containerLocation = container.getLocation(); |
| if (containerLocation != null) { |
| docroot = containerLocation.toString(); |
| } |
| else if (container.getLocationURI() != null) { |
| docroot = container.getLocationURI().toString(); |
| } |
| } |
| if (docroot == null) { |
| docroot = baseModel.getBaseLocation(); |
| } |
| if (docroot == null) { |
| // should not be |
| return null; |
| } |
| |
| // obtain document url |
| String modelBaseLocation = baseModel.getBaseLocation(); |
| if ((modelBaseLocation == null) || (modelBaseLocation.length() == 0)) { |
| // fallback... |
| modelBaseLocation = baseModel.getId(); |
| } |
| if ((modelBaseLocation == null) || (modelBaseLocation.length() == 0)) { |
| // i can't resolve uri ! |
| return null; |
| } |
| |
| // resolve url |
| URLHelper helper = new URLHelper(PathHelper.getContainingFolderPath(modelBaseLocation), PathHelper.getContainingFolderPath(PathHelper.appendTrailingURLSlash(docroot))); |
| return helper.toAbsolute(ref); |
| } |
| |
| } |
| |