/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.esri.gpt.catalog.gxe;
import com.esri.gpt.framework.context.ConfigurationException;
import com.esri.gpt.framework.util.Val;
import com.esri.gpt.framework.xml.DomUtil;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* A component XML file associated with a Geoportal XML editor definition.
*/
public class GxeFile {
/** class variables ========================================================= */
/** The Logger. */
private static Logger LOGGER = Logger.getLogger(GxeFile.class.getName());
/** instance variables ====================================================== */
private List<URI> baseLocations = new ArrayList<URI>();
private boolean isRoot = false;
private String location;
private GxeFile parent;
private URL referencedUrl;
/** constructors ============================================================ */
/** Default constructor */
public GxeFile() {}
/**
* Constructs with a parent file and reference location.
* @param parent the parent file from which this external reference was made
* @param location the file location
*/
public GxeFile(GxeFile parent, String location) {
this.setParent(parent);
this.setLocation(location);
}
/** properties ============================================================== */
/**
* Gets the base locations.
* @return the base locations
*/
public List<URI> getBaseLocations() {
return this.baseLocations;
}
/**
* Sets the base locations.
* @param locations the base locations
*/
public void setBaseLocations(List<URI> locations) {
this.baseLocations = locations;
}
/**
* Indicates whether or not the file is the definition root.
* @return true if this is the root
*/
public boolean getIsRoot() {
return this.isRoot;
}
/**
* Indicates whether or not the file is the definition root.
* @param isRoot true if this is the root
*/
public void setIsRoot(boolean isRoot) {
this.isRoot = isRoot;
}
/**
* Gets the location.
* @return the location
*/
public String getLocation() {
return this.location;
}
/**
* Sets the location.
* @param location the location
*/
public void setLocation(String location) {
this.location = location;
}
/**
* Gets the parent.
* @return the parent
*/
public GxeFile getParent() {
return this.parent;
}
/**
* Sets the parent.
* @param parent the parent
*/
public void setParent(GxeFile parent) {
this.parent = parent;
}
/**
* Gets the URL that was referenced while loading the DOM.
* @return the referenced URL
*/
public URL getReferencedUrl() {
return this.referencedUrl;
}
/** methods ================================================================= */
/**
* Finds the root node within a document.
* @param dom the document
* @return the root node
*/
public Node findRoot(Document dom) {
NodeList nl = dom.getChildNodes();
for (int i=0; i<nl.getLength(); i++) {
Node nd = nl.item(i);
if (nd.getNodeType() == Node.ELEMENT_NODE) {
return nd;
}
}
return null;
}
/**
* Establishes the base location inheritance precedence.
* @param dom the active XML document.
* @throws Exception if an exception occurs
*/
private void inheritBaseLocations(Document dom) throws Exception {
String ns = GxeContext.URI_GXE;
Node root = this.findRoot(dom);
Node nd = root.getAttributes().getNamedItemNS(ns,"extends");
if (nd != null) {
String loc = Val.chkStr(nd.getNodeValue());
if (loc.length() > 0) {
URL locUrl = Thread.currentThread().getContextClassLoader().getResource(loc);
if (locUrl != null) {
InputSource src = new InputSource(locUrl.toExternalForm());
Document domExtends = DomUtil.makeDomFromSource(src,true);
String baseLoc = locUrl.toExternalForm();
int nIdx = baseLoc.lastIndexOf("/");
if (nIdx != -1) baseLoc = baseLoc.substring(0,nIdx);
this.getBaseLocations().add(new URI(baseLoc+"/"));
this.inheritBaseLocations(domExtends);
}
}
}
}
/**
* Loads the XML document object model (org.w3c.dom.Document).
* @throws Exception if an exception occurs
*/
public Document loadDom() {
Document dom = null;
try {
URL url = this.makeUrl();
InputSource src = new InputSource(url.toExternalForm());
dom = DomUtil.makeDomFromSource(src,true);
if (this.getIsRoot()) {
this.setBaseLocations(new ArrayList<URI>());
String baseLoc = url.toExternalForm();
int nIdx = baseLoc.lastIndexOf("/");
if (nIdx != -1) baseLoc = baseLoc.substring(0,nIdx);
this.getBaseLocations().add(new URI(baseLoc+"/"));
this.inheritBaseLocations(dom);
}
} catch (Exception e) {
String msg = "Error loading XML file: "+Val.chkStr(this.getLocation());
msg += ", "+Val.chkStr(e.toString());
LOGGER.log(Level.CONFIG,msg,e);
throw new ConfigurationException(msg,e);
}
return dom;
}
/**
* Returns the URL for the file.
* @return the associated url
* @throws IOException if the URL cannot be generated
*/
public URL makeUrl() throws Exception {
// return the reference if it was already established
if (this.referencedUrl != null) return this.referencedUrl;
String loc = Val.chkStr(this.getLocation());
URL locUrl = null;
// determine the base location URIs
URI rootUri = null;
List<URI> baseUris = null;
GxeFile parentFile = this;
while (parentFile != null) {
if (parentFile.isRoot) {
baseUris = parentFile.getBaseLocations();
break;
}
parentFile = parentFile.getParent();
}
boolean hasBaseUris = (baseUris != null) && (baseUris.size() > 0);
if (hasBaseUris) rootUri = baseUris.get(0);
if (locUrl == null) {
locUrl = Thread.currentThread().getContextClassLoader().getResource(loc);
}
if ((locUrl == null) && !loc.startsWith("$base")) {
if ((this.getParent() != null) && (this.getParent().getReferencedUrl() != null)) {
try {
URL parentUrl = this.getParent().getReferencedUrl();
URI parentUri = parentUrl.toURI();
URI thisUri = parentUri.resolve(loc);
File thisFile = new File(thisUri);
locUrl = thisUri.toURL();
if (!thisFile.exists() && hasBaseUris && (rootUri != null)) {
URI relUri = rootUri.relativize(thisUri);
int n = baseUris.size();
for (int i=0; i<n; i++) {
URI testUri = baseUris.get(i).resolve(relUri);
// this won't work for http based URIs
if ((new File(testUri)).exists()) {
locUrl = testUri.toURL();
break;
}
}
}
} catch (URISyntaxException e) {
LOGGER.log(Level.CONFIG,"While attemting to load: "+loc+", error: "+e.toString(),e);
}
}
}
if ((locUrl == null) && loc.startsWith("$base")) {
String relLoc = Val.chkStr(loc.substring(5));
if (relLoc.startsWith("/")) relLoc = Val.chkStr(relLoc.substring(1));
if ((relLoc.length() > 0) && hasBaseUris) {
try {
URI relUri = new URI(relLoc);
int n = baseUris.size();
for (int i=0; i<n; i++) {
URI testUri = baseUris.get(i).resolve(relUri);
// this won't work for http based URIs
if ((new File(testUri)).exists()) {
locUrl = testUri.toURL();
break;
}
}
} catch (URISyntaxException e) {
LOGGER.log(Level.CONFIG,"While attemting to load: "+loc+", error: "+e.toString(),e);
}
}
}
// throw an exception if the URL was not generated, otherwise save and return the reference
if (locUrl == null) {
throw new IOException("Unable to create resource URL for path: "+loc);
} else {
this.referencedUrl = locUrl;
}
return this.referencedUrl;
}
}