/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed 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.anodyneos.xp.tag.core;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.LinkedList;
import java.util.List;
import javax.servlet.jsp.el.ELException;
import org.anodyneos.xp.XpException;
import org.anodyneos.xp.XpOutput;
import org.anodyneos.xp.tagext.XpTag;
import org.anodyneos.xp.tagext.XpTagSupport;
import org.xml.sax.SAXException;
/**
* <p>
* Support for tag handlers for <param>, the URL parameter subtag for
* <import> in JSTL 1.0.
* </p>
*
* @see ParamParent, ImportSupport, URLEncodeSupport
* @author Shawn Bayern
*/
public class ParamTag extends XpTagSupport {
// *********************************************************************
// Protected state
protected String name; // 'name' attribute
protected String value; // 'value' attribute
// *********************************************************************
// Constructor and initialization
public ParamTag() {
name = value = null;
}
// *********************************************************************
// Tag logic
// simply send our name and value to our appropriate ancestor
public void doTag(XpOutput out) throws XpException, SAXException,
ELException {
XpTag t = findAncestorWithClass(this, ParamParent.class);
if (t == null) {
throw new XpException("No ParamParent found.");
}
// take no action for null or empty names
if (name == null || name.equals("")) {
return;
}
// send the parameter to the appropriate ancestor
ParamParent parent = (ParamParent) t;
String value = this.value;
if (value == null) {
if (getXpBody() == null) {
value = "";
} else {
value = getXpBody().invokeToString().trim();
}
}
parent.addParameter(name, value);
}
// for tag attribute
public void setName(String name) {
this.name = name;
}
// for tag attribute
public void setValue(String value) {
this.value = value;
}
// *********************************************************************
// Support for parameter management
/**
* Provides support for aggregating query parameters in URLs. Specifically,
* accepts a series of parameters, ensuring that - newer parameters will
* precede older ones in the output URL - all supplied parameters precede
* those in the input URL
*/
public static class ParamManager {
// *********************************
// Private state
private List names = new LinkedList();
private List values = new LinkedList();
private boolean done = false;
// *********************************
// Public interface
/** Adds a new parameter to the list. */
public void addParameter(String name, String value) {
if (done)
throw new IllegalStateException();
if (name != null) {
names.add(name);
if (value != null)
values.add(value);
else
values.add("");
}
}
/**
* Produces a new URL with the stored parameters, in the appropriate
* order.
*/
public String aggregateParams(String url, String fragment, String encoding)
throws UnsupportedEncodingException {
if (done) {
throw new IllegalStateException();
}
done = true;
if (names.isEmpty() && (null == fragment)) {
return url;
}
int poundIndex = url.indexOf('#');
int questionMarkIndex = url.indexOf('?');
if (questionMarkIndex > poundIndex) {
questionMarkIndex = -1;
}
// fragment
String workingFragment = null;
if (null != fragment) {
workingFragment = URLEncoder.encode(fragment, encoding);
} else if (-1 != poundIndex) {
workingFragment = url.substring(poundIndex + 1);
}
// oldQuery
String oldQuery = null;
if (-1 != questionMarkIndex) {
if (-1 == poundIndex) {
oldQuery = url.substring(questionMarkIndex + 1);
} else {
oldQuery = url.substring(questionMarkIndex + 1, poundIndex);
}
}
// build the URL
StringBuilder workingUrl = new StringBuilder();
// base
if (-1 == poundIndex && -1 == questionMarkIndex) {
workingUrl.append(url);
} else if (-1 != questionMarkIndex) {
workingUrl.append(url.substring(0, questionMarkIndex));
} else if (-1 != poundIndex) {
workingUrl.append(url.substring(0, poundIndex));
}
// newQuery
boolean queryStarted = false;
if (! names.isEmpty()) {
workingUrl.append('?');
queryStarted = true;
for (int i = 0; i < names.size(); i++) {
workingUrl.append(URLEncoder.encode((String) names.get(i), encoding));
workingUrl.append('=');
workingUrl.append(URLEncoder.encode((String) values.get(i), encoding));
if (i < (names.size() - 1)) {
workingUrl.append("&");
}
}
}
// old query params
if (null != oldQuery && oldQuery.length() > 0) {
if (! queryStarted) {
workingUrl.append('?');
} else {
workingUrl.append('&');
}
workingUrl.append(oldQuery);
}
// fragment
if (null != workingFragment && workingFragment.length() > 0) {
workingUrl.append("#");
workingUrl.append(workingFragment);
}
return workingUrl.toString();
}
}
}