blob: c3d67ff243c6648b613bb98d2cb4c9ccfe7f5915 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2018 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Jens Lukowski/Innoopract - initial renaming/restructuring
*
*******************************************************************************/
package org.eclipse.wst.xml.core.internal.contentmodel.modelqueryimpl;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.CRC32;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.wst.sse.core.utils.StringUtils;
import org.eclipse.wst.xml.core.internal.Logger;
import org.eclipse.wst.xml.core.internal.XMLCorePlugin;
import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.CMDocumentManager;
import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.IExternalSchemaLocationProvider;
import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery;
import org.eclipse.wst.xml.core.internal.contentmodel.util.CMDocumentCache;
import org.eclipse.wst.xml.core.internal.contentmodel.util.NamespaceInfo;
import org.eclipse.wst.xml.core.internal.contentmodel.util.NamespaceTable;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
*
*/
public class CMDocumentLoader
{
private final static boolean _trace = Boolean.valueOf(Platform.getDebugOption("org.eclipse.wst.xml.core/externalSchemaLocation")).booleanValue(); //$NON-NLS-1$
protected Document document;
protected ModelQuery modelQuery;
protected CMDocumentManager cmDocumentManager;
protected boolean isInferredGrammarEnabled = true;
protected CMDocumentLoadingNamespaceTable namespaceTable;
protected int count = 0;
public CMDocumentLoader(Document document, ModelQuery modelQuery)
{
this(document, modelQuery.getCMDocumentManager());
}
public CMDocumentLoader(Document document, CMDocumentManager cmDocumentManager)
{
this.document = document;
this.cmDocumentManager = cmDocumentManager;
}
public void loadCMDocuments()
{
//System.out.println("----------loadCMDocuments ------------");
//long time = System.currentTimeMillis();
boolean walkDocument = false;
cmDocumentManager.removeAllReferences();
String[] doctypeInfo = XMLAssociationProvider.getDoctypeInfo(document);
if (doctypeInfo != null)
{
// load the doctype if required
walkDocument = handleGrammar(doctypeInfo[0], doctypeInfo[1], "DTD"); //$NON-NLS-1$
}
else
{
if (document.getDoctype() != null) {
final String internalSubset = document.getDoctype().getInternalSubset();
if (internalSubset != null) {
IPath path = getInternalSubsetPath(document);
if (!Path.EMPTY.equals(path)) {
File subsets = path.removeLastSegments(1).toFile();
if (!subsets.exists()) {
subsets.mkdir();
}
FileOutputStream stream = null;
try {
stream = new FileOutputStream(path.toFile());
final String charset = ((IFile) ((IAdaptable) document).getAdapter(IResource.class)).getCharset();
stream.write(internalSubset.getBytes(charset != null ? charset : "UTF-8")); //$NON-NLS-1$
stream.flush();
} catch (FileNotFoundException e) {
} catch (CoreException e) {
} catch (UnsupportedEncodingException e) {
} catch (IOException e) {
}
finally {
if (stream != null) {
try {
stream.close();
}
catch (IOException e) { }
}
}
walkDocument = handleGrammar(path.toPortableString(), path.toFile().toURI().toString(), "DTD"); //$NON-NLS-1$
}
}
}
else {
Element element = getRootElement(document);
if (element != null)
{
namespaceTable = new CMDocumentLoadingNamespaceTable(document);
namespaceTable.addElement(element);
if (namespaceTable.isNamespaceEncountered())
{
walkDocument = true;
//System.out.println("isNamespaceAware");
}
else
{
namespaceTable = null;
walkDocument = isInferredGrammarEnabled;
//System.out.println("is NOT namespaceAware");
}
}
}
}
if (walkDocument)
{
if (!checkExternalSchema())
visitNode(document);
}
//System.out.println("--- elapsed time (" + count + ") = " + (System.currentTimeMillis() - time));
}
protected boolean checkExternalSchema() {
boolean externalSchemaLoaded = false;
if (document instanceof IDOMDocument) {
final String baseLocation = ((IDOMDocument) document).getModel().getBaseLocation();
if (baseLocation == null)
return false;
final IPath basePath = new Path(baseLocation);
IFile file = null;
if (basePath.segmentCount() > 1) {
file = ResourcesPlugin.getWorkspace().getRoot().getFile(basePath);
}
final URI uri = (file == null || !file.isAccessible()) ? new File(baseLocation).toURI() : file.getLocationURI();
if (uri != null) {
IExternalSchemaLocationProvider[] providers = ExternalSchemaLocationProviderRegistry.getInstance().getProviders();
for (int i = 0; i < providers.length; i++) {
long time = _trace ? System.currentTimeMillis(): 0;
final Map locations = providers[i].getExternalSchemaLocation(uri);
if (_trace) {
long diff = System.currentTimeMillis() - time;
if (diff > 250)
Logger.log(Logger.INFO, "Schema location provider took [" + diff + "ms] for URI [" + uri + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
if (locations != null && !locations.isEmpty()) {
// Use the externalSchemaLocation
if (namespaceTable != null && namespaceTable.isNamespaceEncountered()) {
Object schemaLocation = locations.get(IExternalSchemaLocationProvider.SCHEMA_LOCATION);
if (schemaLocation != null) {
final String location = schemaLocation.toString();
if (location != null) {
final String[] ids = StringUtils.asArray(location);
// namespace : location pairs
if (ids.length >= 2 && ids.length % 2 == 0) {
if (!externalSchemaLoaded)
cmDocumentManager.removeAllReferences();
for (int j = 0; j < ids.length; j+=2) {
handleGrammar(ids[j], ids[j + 1], "XSD"); //$NON-NLS-1$
externalSchemaLoaded = true;
}
}
}
else {
Logger.log(Logger.ERROR_DEBUG, "External schema location provider did not return an external schema location for IExternalSchemaLocationProvider.SCHEMA_LOCATION: " + providers[i].getClass().getName()); //$NON-NLS-1$
}
}
}
else { // noNamespace
Object schemaLocation = locations.get(IExternalSchemaLocationProvider.NO_NAMESPACE_SCHEMA_LOCATION);
if (schemaLocation != null) {
handleGrammar(uri.toString(), schemaLocation.toString(), "XSD"); //$NON-NLS-1$
externalSchemaLoaded = true;
break;
}
else {
Logger.log(Logger.ERROR_DEBUG, "External schema location provider did not return an external schema location for IExternalSchemaLocationProvider.NO_NAMESPACE_SCHEMA_LOCATION: " + providers[i].getClass().getName()); //$NON-NLS-1$
}
}
}
}
}
}
return externalSchemaLoaded;
}
public boolean handleGrammar(String publicId, String systemId, String type)
{
boolean result = false;
int status = cmDocumentManager.getCMDocumentStatus(publicId);
if (status == CMDocumentCache.STATUS_NOT_LOADED)
{
cmDocumentManager.addCMDocumentReference(publicId, systemId, type);
}
else if (status == CMDocumentCache.STATUS_ERROR)
{
result = true;
}
return result;
}
public void handleElement(Element element)
{
visitChildNodes(element);
}
public void handleElementNS(Element element)
{
namespaceTable.addElement(element);
visitChildNodes(element);
}
public void visitNode(Node node)
{
int nodeType = node.getNodeType();
if (nodeType == Node.ELEMENT_NODE)
{
count++;
Element element = (Element)node;
if (namespaceTable == null)
{
handleElement(element);
}
else
{
handleElementNS(element);
}
}
else if (nodeType == Node.DOCUMENT_NODE)
{
visitChildNodes(node);
}
}
protected void visitChildNodes(Node node)
{
for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling())
{
visitNode(child);
}
}
protected class CMDocumentLoadingNamespaceTable extends NamespaceTable
{
protected List newNamespaceList;
public CMDocumentLoadingNamespaceTable(Document document)
{
super(document);
}
public void addElement(Element element)
{
newNamespaceList = null;
super.addElement(element);
if (newNamespaceList != null)
{
for (Iterator i = newNamespaceList.iterator(); i.hasNext(); )
{
NamespaceInfo info = (NamespaceInfo)i.next();
handleGrammar(info.uri, info.locationHint, "XSD"); //$NON-NLS-1$
}
}
}
protected void internalAddNamespaceInfo(String key, NamespaceInfo info)
{
super.internalAddNamespaceInfo(key, info);
if (newNamespaceList == null)
{
newNamespaceList = new ArrayList();
}
newNamespaceList.add(info);
}
}
protected Element getRootElement(Document document)
{
Element result = null;
NodeList nodeList = document.getChildNodes();
int nodeListLength = nodeList.getLength();
for (int i = 0 ; i < nodeListLength; i++)
{
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE)
{
result = (Element)node;
break;
}
}
return result;
}
static IPath getInternalSubsetPath(Document document) {
IPath path = Path.EMPTY;
if (document instanceof IAdaptable) {
final IResource resource = ((IAdaptable) document).getAdapter(IResource.class);
if (resource instanceof IFile) {
CRC32 calc = new CRC32();
calc.update(resource.getFullPath().toString().getBytes());
path = XMLCorePlugin.getDefault().getStateLocation().append("internalsubsets").append(calc.getValue() + ".dtd"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return path;
}
}