package org.w3c.jigsaw.frames;
import java.io.*;
import java.util.*;
import org.w3c.tools.resources.*;
import org.w3c.jigsaw.http.* ;
import org.w3c.jigsaw.html.*;
import org.w3c.jigsaw.html.HtmlGenerator ;
import org.w3c.www.mime.* ;
import org.w3c.www.http.*;
import org.w3c.tools.resources.ProtocolException;
import org.w3c.tools.resources.NotAProtocolException;
public class NegotiatedFrame extends HTTPFrame {
class VariantState {
ResourceReference variant = null ;
double qs = 0.0 ;
double qe = 0.0 ;
double qc = 0.0 ;
double ql = 0.0 ;
double q = 0.0 ; double Q = 0.0 ;
public String toString() {
try {
Resource res = variant.lock();
String name = (String) res.getIdentifier() ;
if ( name == null )
name = "<noname>" ;
return "[" + name
+ " qs=" + qs
+ " qe=" + qe
+ " ql=" + ql
+ " q =" + q
+ " Q =" + getQ()
+"]" ;
} catch (InvalidResourceException ex) {
return "invalid";
} finally {
variant.unlock();
}
}
void setContentEncodingQuality (double qe) {
this.qe = qe ;
}
void setQuality (double q) {
this.q = q ;
}
void setQuality (HttpAccept a) {
q = a.getQuality() ;
}
void setLanguageQuality (double ql) {
this.ql = ql ;
}
void setLanguageQuality (HttpAcceptLanguage l) {
this.ql = l.getQuality() ;
}
double getLanguageQuality () {
return ql ;
}
ResourceReference getResource () {
return variant ;
}
double getQ() {
return qe * q * qs * ql ;
}
VariantState (ResourceReference variant, double qs) {
this.qs = qs ;
this.variant = variant ;
}
}
private static Class httpFrameClass = null;
static {
try {
httpFrameClass = Class.forName("org.w3c.jigsaw.frames.HTTPFrame") ;
} catch (Exception ex) {
throw new RuntimeException("No HTTPFrame class found.");
}
}
private static final boolean debug = false;
private static final double REQUIRED_QUALITY = 0.0001 ;
protected static HttpTokenList VARY = null;
protected static int ATTR_VARIANTS = -1 ;
static {
String vary[] = { "Accept",
"Accept-Charset",
"Accept-Language",
"Accept-Encoding" };
VARY = HttpFactory.makeStringList(vary);
}
static {
Attribute a = null ;
Class cls = null ;
try {
cls = Class.forName("org.w3c.jigsaw.frames.NegotiatedFrame") ;
} catch (Exception ex) {
ex.printStackTrace() ;
System.exit(1) ;
}
a = new StringArrayAttribute("variants"
, null
, Attribute.EDITABLE) ;
ATTR_VARIANTS = AttributeRegistry.registerAttribute(cls, a) ;
}
public String[] getVariantNames() {
return (String[]) getValue(ATTR_VARIANTS, null) ;
}
public void setVariants(String variants[]) {
setValue(ATTR_VARIANTS, variants);
}
@return @exception ProtocolException
public ResourceReference[] getVariantResources()
throws ProtocolException
{
String names[] = getVariantNames() ;
if ( names == null )
return null ;
ResourceReference variants[] = new ResourceReference[names.length] ;
ResourceReference r_parent = resource.getParent() ;
try {
DirectoryResource parent = (DirectoryResource) r_parent.lock();
for (int i = 0 ; i < names.length ; i++) {
variants[i] = parent.lookup(names[i]) ;
if (variants[i] == null)
throw new HTTPException(names[i]+
": couldn't be restored.");
}
} catch (InvalidResourceException ex) {
throw new HTTPException("invalid parent for negotiation");
} finally {
r_parent.unlock();
}
return variants ;
}
@param header@param states
protected void printNegotiationState (String header, Vector states) {
if ( debug ) {
System.out.println ("------" + header) ;
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
System.out.println (state) ;
}
System.out.println ("-----") ;
}
}
@param states@param request
protected boolean negotiateContentEncoding (Vector states,
Request request)
throws ProtocolException
{
if ( ! request.hasAcceptEncoding() ) {
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
state.setContentEncodingQuality(1.0) ;
}
} else {
String encodings[] = request.getAcceptEncoding() ;
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
ResourceReference rr = state.getResource();
try {
FramedResource resource = (FramedResource)rr.lock() ;
HTTPFrame itsframe =
(HTTPFrame) resource.getFrame(httpFrameClass);
if (itsframe != null) {
if ( !itsframe.definesAttribute("content-encoding") ) {
state.setContentEncodingQuality (1.0) ;
} else {
String ve = itsframe.getContentEncoding() ;
state.setContentEncodingQuality (0.001) ;
encoding_loop:
for (int j = 0 ; j < encodings.length ; j++) {
if ( ve.equals (encodings[j]) ) {
state.setContentEncodingQuality(1.0) ;
break encoding_loop ;
}
}
}
}
} catch (InvalidResourceException ex) {
} finally {
rr.unlock();
}
}
}
return false ;
}
@param states@param request
protected boolean negotiateCharsetQuality (Vector states
, Request request) {
return false ;
}
@param states@param request
protected boolean negotiateLanguageQuality (Vector states
, Request request)
throws ProtocolException
{
if ( ! request.hasAcceptLanguage() ) {
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
state.setLanguageQuality (1.0) ;
}
} else {
HttpAcceptLanguage languages[] = request.getAcceptLanguage() ;
boolean varyLang = false ;
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
ResourceReference rr = state.getResource();
try {
FramedResource resource = (FramedResource)rr.lock() ;
HTTPFrame itsframe =
(HTTPFrame) resource.getFrame(httpFrameClass);
if (itsframe != null) {
if ( !itsframe.definesAttribute("content-language") ) {
state.setLanguageQuality (-1.0) ;
} else {
varyLang = true ;
String lang = itsframe.getContentLanguage() ;
int jidx = -1 ;
for (int j = 0 ; j < languages.length ; j++) {
if ( languages[j].getLanguage().equals(lang) )
jidx = j ;
}
if ( jidx < 0 )
state.setLanguageQuality(0.001) ;
else
state.setLanguageQuality (languages[jidx]) ;
}
}
} catch (InvalidResourceException ex) {
} finally {
rr.unlock();
}
}
if ( varyLang ) {
for (int i = 0 ; i < states.size() ; i++) {
VariantState s = (VariantState) states.elementAt(i);
if ( s.getLanguageQuality() < 0 )
s.setLanguageQuality (0.5) ;
}
} else {
for (int i = 0 ; i < states.size() ; i++) {
VariantState s = (VariantState) states.elementAt(i) ;
s.setLanguageQuality (1.0) ;
}
}
}
return false ;
}
@param states@param request
protected boolean negotiateContentType (Vector states,
Request request)
throws ProtocolException
{
if ( ! request.hasAccept() ) {
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
state.setQuality (1.0) ;
}
} else {
HttpAccept accepts[] = request.getAccept() ;
for (int i = 0 ; i < states.size() ; i++ ) {
VariantState state = (VariantState) states.elementAt(i) ;
ResourceReference rr = state.getResource();
try {
FramedResource resource = (FramedResource)rr.lock() ;
HTTPFrame itsframe =
(HTTPFrame) resource.getFrame(httpFrameClass);
if (itsframe != null) {
MimeType vt = itsframe.getContentType();
int jmatch = -1 ;
int jidx = -1 ;
for (int j = 0 ; j < accepts.length ; j++) {
int match = vt.match (accepts[j].getMimeType()) ;
if ( match > jmatch ) {
jmatch = match ;
jidx = j ;
}
}
if ( jidx < 0 )
state.setQuality (0.0) ;
else
state.setQuality(accepts[jidx]) ;
}
} catch (InvalidResourceException ex) {
} finally {
rr.unlock();
}
}
}
return false ;
}
protected ResourceReference negotiate (Request request)
throws ProtocolException
{
ResourceReference variants[] = getVariantResources() ;
if ( variants.length < 2 ) {
if ( variants.length == 0 ) {
Reply reply = request.makeReply(HTTP.NOT_ACCEPTABLE) ;
reply.setContent ("<p>No acceptable variants.") ;
throw new HTTPException (reply) ;
} else {
return variants[0] ;
}
}
Vector states = new Vector (variants.length) ;
for (int i = 0 ; i < variants.length ; i++) {
double qs = 1.0 ;
try {
FramedResource resource = (FramedResource)variants[i].lock() ;
HTTPFrame itsframe =
(HTTPFrame) resource.getFrame(httpFrameClass);
if (itsframe != null) {
if ( itsframe.definesAttribute ("quality") )
qs = itsframe.getQuality() ;
if ( qs > REQUIRED_QUALITY )
states.addElement(new VariantState (variants[i], qs)) ;
}
} catch (InvalidResourceException ex) {
} finally {
variants[i].unlock();
}
}
if ( debug )
printNegotiationState ("init:", states) ;
if ( negotiateContentEncoding (states, request) )
return ((VariantState) states.elementAt(0)).getResource() ;
if ( debug )
printNegotiationState ("encoding:", states) ;
if ( negotiateCharsetQuality (states, request) )
return ((VariantState) states.elementAt(0)).getResource() ;
if ( debug )
printNegotiationState ("charset:", states) ;
if ( negotiateLanguageQuality (states, request) )
return ((VariantState) states.elementAt(0)).getResource() ;
if ( debug )
printNegotiationState ("language:", states) ;
if ( negotiateContentType (states, request) )
return ((VariantState) states.elementAt(0)).getResource() ;
if ( debug )
printNegotiationState ("type:", states) ;
if ( debug )
printNegotiationState ("before Q selection:", states) ;
double qmax = REQUIRED_QUALITY ;
for (int i = 0 ; i < states.size() ; ) {
VariantState state = (VariantState) states.elementAt(i) ;
if ( state.getQ() > qmax ) {
for (int j = i ; j > 0 ; j--)
states.removeElementAt(0) ;
qmax = state.getQ() ;
i = 1 ;
} else {
states.removeElementAt(i) ;
}
}
if ( debug )
printNegotiationState ("After Q selection:", states) ;
if ( qmax == REQUIRED_QUALITY ) {
Reply reply = request.makeReply(HTTP.NOT_ACCEPTABLE) ;
reply.setContent ("<p>No acceptable variant.") ;
throw new HTTPException (reply) ;
} else if ( states.size() == 1 ) {
return ((VariantState) states.elementAt(0)).getResource() ;
} else {
Reply reply = request.makeReply(HTTP.MULTIPLE_CHOICE) ;
HtmlGenerator g = new HtmlGenerator ("Multiple choice for "+
resource.getIdentifier()) ;
g.append ("<ul>") ;
for (int i = 0 ; i < states.size() ; i++) {
VariantState state = (VariantState) states.elementAt(i) ;
String name = null;
ResourceReference rr = state.getResource();
try {
name = rr.lock().getIdentifier();
g.append ("<li>"
+ "<a href=\"" + name + "\">" + name + "</a>"
+ " Q= " + state.getQ()) ;
} catch (InvalidResourceException ex) {
} finally {
rr.unlock();
}
}
reply.setStream (g) ;
throw new HTTPException (reply) ;
}
}
public void registerResource(FramedResource resource) {
super.registerOtherResource(resource);
}
@param request@exception ProtocolException
public ReplyInterface perform(RequestInterface req)
throws ProtocolException, NotAProtocolException
{
ReplyInterface repi = performFrames(req);
if (repi != null)
return repi;
if (! checkRequest(req))
return null;
Request request = (Request) req;
ResourceReference selected = negotiate(request) ;
if ( selected == null ) {
Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;
error.setContent("Error negotiating among resource's variants.");
throw new HTTPException(error) ;
}
try {
FramedResource resource = (FramedResource) selected.lock();
Reply reply = (Reply)resource.perform(request) ;
reply.setHeaderValue(reply.H_VARY, VARY);
HTTPFrame itsframe =
(HTTPFrame) resource.getFrame(httpFrameClass);
if (itsframe != null) {
reply.setContentLocation(
itsframe.getURL(request).toExternalForm()) ;
return reply;
}
Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;
error.setContent("Error negotiating : "+
"selected resource has no HTTPFrame");
throw new HTTPException(error) ;
} catch (InvalidResourceException ex) {
Reply error = request.makeReply(HTTP.INTERNAL_SERVER_ERROR) ;
error.setContent("Error negotiating : Invalid selected resource");
throw new HTTPException(error) ;
} finally {
selected.unlock();
}
}
}