blob: 43a6f5ac688cf8c184c0ce14474099a79d504ab1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.cocoon.bean;
import java.util.TreeMap;
import org.apache.cocoon.Constants;
import org.apache.cocoon.util.MIMEUtils;
import org.apache.cocoon.util.NetUtils;
import org.apache.cocoon.ProcessingException;
/**
* A Target is a single page for generation. It encapsulates the URI
* arithmetic required to transform the URI of the page to be generated
* (the source URI) into the URI to which the resulting page should be
* written (the destination URI).
*
* @author <a href="mailto:uv@upaya.co.uk">Upayavira</a>
* @version CVS $Id$
*/
public class Target {
// Defult type is append
private static final String APPEND_TYPE = "append";
private static final String REPLACE_TYPE = "replace";
private static final String INSERT_TYPE = "insert";
private final String type;
private final String root;
private final String sourceURI;
private final String destURI;
private final String deparameterizedSourceURI;
private final TreeMap parameters;
private String parentURI = null;
private String originalURI = null;
private String mimeType = null;
private String defaultFilename = Constants.INDEX_URI;
private String finalDestinationURI = null;
private String extension = null;
private boolean followLinks;
private boolean confirmExtension;
private String logger;
private transient int _hashCode;
private transient String _toString;
public Target(String type,
String root,
String sourceURI,
String destURI)
throws IllegalArgumentException {
this.type = type;
this.root = root;
if (destURI == null || destURI.length() == 0) {
throw new IllegalArgumentException("You must specify a destination directory when defining a target");
}
if (!destURI.endsWith("/")) {
destURI += "/";
}
this.destURI = destURI;
this.parameters = new TreeMap();
// Normalize sourceURI, and make sure that parameters is always in the same order
sourceURI = NetUtils.normalize(root + sourceURI);
this.deparameterizedSourceURI = NetUtils.deparameterize(sourceURI, this.parameters);
this.sourceURI = NetUtils.parameterize(this.deparameterizedSourceURI, this.parameters);
}
public Target(String type, String sourceURI, String destURI)
throws IllegalArgumentException {
this(type, "", sourceURI, destURI);
}
public Target(String sourceURI, String destURI)
throws IllegalArgumentException {
this(APPEND_TYPE, "", sourceURI, destURI);
}
public Target getDerivedTarget(String originalLinkURI)
throws IllegalArgumentException {
String linkURI = originalLinkURI;
// Fix relative links starting with "?"
if (linkURI.startsWith("?")) {
linkURI = this.getPageURI() + linkURI;
}
linkURI =
NetUtils.normalize(NetUtils.absolutize(this.getPath(), linkURI));
// Ignore pages outside the root folder
if (!linkURI.startsWith(this.root)) {
return null;
}
linkURI = linkURI.substring(root.length());
Target target = new Target(this.type, this.root, linkURI, this.destURI);
target.setOriginalURI(originalLinkURI);
target.setParentURI(this.sourceURI);
target.setConfirmExtension(this.confirmExtension);
target.setFollowLinks(this.followLinks);
target.setDefaultFilename(this.defaultFilename);
target.setLogger(this.logger);
return target;
}
/**
* Sets the original URI. This is used to record the URI that
* caused the creation of this Target, for example as a link
* in another page. It is needed for doing link translation, as
* this is the URI that must be replaced by the translated one.
*/
public void setOriginalURI(String uri) {
this.originalURI = uri;
}
/**
* Sets the URI of the page that contained the link to this
* URI. Used for reporting purposes.
*/
public void setParentURI(String uri) {
this.parentURI = uri;
}
/**
* Sets the mime type for the resource referenced by this target.
* If a mime type is specified, the file extension of the
* destination URI will be checked to see that it matches the
* default extension for the specified mime type. If it doesn't,
* the default extension will be appended to the destination URI.
*
* This URI change will be taken into account in pages that link
* to the current page.
*
* If the mime type is not specified (and thus null), no extension
* checking will take place.
*/
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
this.finalDestinationURI = null;
}
/**
* Sets a file extension to be appended to the end of the destination
* URI. The main use of this is to create broken link error files that
* stand out, within the file structure of the generated site, by, for
* example, adding '.error' to the end of the filename.
*/
public void setExtraExtension(String extension) {
this.extension = extension;
this.finalDestinationURI = null;
}
/**
* Sets the default filename. This filename is appended to URIs
* that refer to a directory, i.e. end with a slash, as resources
* referred to by such a URI cannot be written to a file system
* without a filename.
*
* This URI change will be taken into account in pages that link
* to the current page.
*
* If no default is specified, the Cocoon constants value will
* be used.
*/
public void setDefaultFilename(String filename) {
this.defaultFilename = filename;
}
/**
* Gets the filename from the source URI, without the path.
* This is used to fill out relative URIs that have
* parameters but no filename such as ?page=123
*/
public String getPageURI() {
String pageURI = this.getSourceURI();
if (pageURI.indexOf("/") != -1) {
pageURI = pageURI.substring(pageURI.lastIndexOf("/") + 1);
if (pageURI.length() == 0) {
pageURI = "./";
}
}
return pageURI;
}
/**
* Gets the path from the source URI, without the filename.
* This is used when absolutizing/relativizing link URIs.
*/
public String getPath() {
return NetUtils.getPath(this.getSourceURI());
}
/**
* Gets the file extension for the source URI
*/
public String getExtension() {
return NetUtils.getExtension(this.getSourceURI());
}
/**
* Gets the parent URI (the URI of the page that contained
* a link to this URI). null is returned if this page was
* not referred to in a link.
*/
public String getParentURI() {
return this.parentURI;
}
/**
* Calculates the destination URI - the URI to which the generated
* page should be written. This will be a URI that, when resolved
* by a SourceResolver, will return a modifiableSource.
*
* This calculation is only done once per target. It is therefore
* necessary to ensure that the mime type has been set (if required)
* before this method is called.
*/
public String getDestinationURI()
throws ProcessingException {
if (this.finalDestinationURI == null) {
String actualSourceURI = this.sourceURI;
if (!actualSourceURI.startsWith(root)) {
throw new ProcessingException(
"Derived target does not share same root: "
+ actualSourceURI);
}
actualSourceURI = actualSourceURI.substring(root.length());
actualSourceURI = mangle(actualSourceURI);
String destinationURI;
if (APPEND_TYPE.equals(this.type)) {
destinationURI = destURI + actualSourceURI;
} else if (REPLACE_TYPE.equals(this.type)) {
destinationURI = destURI;
} else if (INSERT_TYPE.equals(this.type)) {
int starPos = destURI.indexOf("*");
if (starPos == -1) {
throw new ProcessingException("Missing * in replace mapper uri");
} else if (starPos == destURI.length() - 1) {
destinationURI = destURI.substring(0, starPos) + actualSourceURI;
} else {
destinationURI = destURI.substring(0, starPos)
+ actualSourceURI
+ destURI.substring(starPos + 1);
}
} else {
throw new ProcessingException(
"Unknown mapper type: " + this.type);
}
if (mimeType != null) {
final String ext = NetUtils.getExtension(destinationURI);
final String defaultExt = MIMEUtils.getDefaultExtension(mimeType);
if (defaultExt != null) {
if ((ext == null) || (!ext.equals(defaultExt))) {
destinationURI += defaultExt;
}
}
}
if (this.extension != null) {
destinationURI += this.extension;
}
this.finalDestinationURI = destinationURI;
}
return this.finalDestinationURI;
}
/**
* Gets a translated version of a link, ready for insertion
* into another page as a link. This link needs to be
* relative to the original page.
*/
public String getTranslatedURI(String path)
throws ProcessingException {
String actualSourceURI = this.sourceURI;
if (!actualSourceURI.startsWith(root)) {
return actualSourceURI;
}
actualSourceURI = mangle(actualSourceURI);
if (mimeType != null) {
final String ext = NetUtils.getExtension(actualSourceURI);
final String defaultExt = MIMEUtils.getDefaultExtension(mimeType);
if (defaultExt != null) {
if ((ext == null) || (!ext.equals(defaultExt))) {
actualSourceURI += defaultExt;
}
}
}
return NetUtils.relativize(path, actualSourceURI);
}
/**
*
* @return destination URI after all authentication details have been
* removed
*/
public String getAuthlessDestURI() throws ProcessingException {
return NetUtils.removeAuthorisation(this.getDestinationURI());
}
/**
* Gets the original URI used to create this Target.
* This URI is completely unprocessed.
*/
public String getOriginalSourceURI() {
return this.originalURI;
}
/**
* Gets the source URI for this target, after
* the URI has been 'prepared' by normalisation,
* absolutization and deparameterization followed
* by reparameterization. This final step is to
* ensure that all parameters appear in a consistent
* order. For example page?a=1&b=2 and page?b=2&a=1
* should be considered the same resource, and thus
* have the same sourceURI.
*/
public String getSourceURI() {
return this.sourceURI;
}
/**
* Gets the source URI for this target, with
* parameters removed. This is the URI that is
* to be passed to Cocoon in order to generate
* the page.
*/
public String getDeparameterizedSourceURI() {
return this.deparameterizedSourceURI;
}
/**
* Gets the parameters that have been removed from
* the URI. These need to be passed to Cocoon when
* generating a page.
*/
public TreeMap getParameters() {
return this.parameters;
}
/**
* Mangle a URI.
*
* @param uri a URI to mangle
* @return a mangled URI
*/
private String mangle(String uri) {
if (uri.length()==0 || uri.charAt(uri.length() - 1) == '/') {
uri += defaultFilename;
}
uri = uri.replace('"', '\'');
uri = uri.replace('?', '_');
uri = uri.replace(':', '_');
return uri;
}
public boolean equals(Object o) {
return (o instanceof Target) && o.toString().equals(toString());
}
public int hashCode() {
if (_hashCode == 0) {
return _hashCode = toString().hashCode();
}
return _hashCode;
}
public String toString() {
if (_toString == null) {
return _toString =
"<"
+ type
+ "|"
+ root
+ "|"
+ sourceURI
+ "|"
+ destURI
+ ">";
}
return _toString;
}
/**
* @return boolean
*/
public boolean confirmExtensions() {
return confirmExtension;
}
public boolean followLinks() {
return followLinks;
}
public String getLogger() {
return logger;
}
public void setConfirmExtension(boolean b) {
confirmExtension = b;
}
public void setFollowLinks(boolean b) {
followLinks = b;
}
public void setLogger(String string) {
logger = string;
}
}