001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: StatusCode.java,v 1.2 2008/06/25 05:47:37 qcheng Exp $
026 *
027 */
028
029
030package com.sun.identity.saml.protocol;
031
032import com.sun.identity.shared.xml.XMLUtils;
033
034import com.sun.identity.saml.common.SAMLConstants;
035import com.sun.identity.saml.common.SAMLException;
036import com.sun.identity.saml.common.SAMLRequesterException;
037import com.sun.identity.saml.common.SAMLUtils;
038
039import java.util.Collections;
040import java.util.List;
041import java.util.StringTokenizer;
042
043import org.w3c.dom.Element;
044
045/**
046 * This class represents the <code>StatusCode</code> and
047 * <code>SubStatusCode</code> element. It corresponds to
048 * <code>samlp:StatusCodeType</code> in SAML protocol schema.
049 *
050 * @supported.all.api
051 */
052public class StatusCode {
053
054    private StatusCode subStatusCode    = null;
055    private String value                = null;
056
057    /**
058     * This is the default constructor of <code>StatusCode</code>.
059     */
060    StatusCode() {
061    }
062  
063    /**
064     * Constructs an instance of <code>StatusCode</code> from a DOM element.
065     *
066     * @param statusCode An DOM Element that's rooted by
067     *        <code>&lt;StatusCode&gt;</code>.
068     * @exception SAMLException when an error occurs.
069     */
070    public StatusCode(Element statusCode) throws SAMLException {
071        if (statusCode == null) {
072            SAMLUtils.debug.message("StatusCode: null input.");
073            throw new SAMLRequesterException(
074                        SAMLUtils.bundle.getString("nullInput"));
075        }
076        String tagName = statusCode.getLocalName();
077        if (!tagName.equals("StatusCode")) {
078            SAMLUtils.debug.message("StatusCode: Wrong input: " + tagName);
079            throw new SAMLRequesterException(
080                        SAMLUtils.bundle.getString("wrongInput"));
081        }
082        value = statusCode.getAttribute("Value");
083        if ((value == null) || (value.length() == 0)) {
084            SAMLUtils.debug.message("StatusCode: empty attribute Value.");
085            throw new SAMLRequesterException(
086                        SAMLUtils.bundle.getString("missingAttribute"));
087        }
088        
089        List subCodenl = XMLUtils.getElementsByTagNameNS1(
090                                (Element) statusCode,
091                                SAMLConstants.PROTOCOL_NAMESPACE_URI,
092                                "StatusCode");
093        int length = subCodenl.size();
094        if (length == 1) {
095            subStatusCode = new StatusCode((Element) subCodenl.get(0));
096        } else if (length != 0) {
097            if (SAMLUtils.debug.messageEnabled()) {
098                SAMLUtils.debug.message("StatusCode: Included more than one"
099                        + " <StatusCode> in element " + tagName);
100            }
101            throw new SAMLRequesterException(
102                        SAMLUtils.bundle.getString("moreElement"));
103        }
104    }
105
106    /**
107     * Construct a <code>StatusCode</code> object from a value String and a sub
108     * <code>StatusCode</code>.
109     *
110     * @param value The value of the <code>StatusCode</code>. This could be
111     *        prefixed by <code>samlp:</code>. If it is not prefixed, or
112     *        prefixed by prefix other than <code>samlp:</code>,
113     *        <code>samlp:</code> will be used instead.
114     * @param subCode The optional sub <code>StatusCode</code>.
115     * @exception SAMLException if value string is null, empty, or contains
116     *            wrong value.
117     */
118    public StatusCode(String value, StatusCode subCode) throws SAMLException {
119        this.value = checkAndGetValue(value);
120        subStatusCode = subCode;
121    }
122
123    /**
124     * Construct a <code>StatusCode</code> object from a value String.
125     *
126     * @param value The value of the <code>StatusCode</code>. This could be
127     *        prefixed by <code>samlp:</code>. It it is not prefixed, or
128     *        prefixed by prefix other than <code>samlp:</code>,
129     *        <code>samlp:</code> will be used instead.
130     * @exception SAMLException if value string is null, empty, or contains
131     *            wrong value.
132     */
133    public StatusCode(String value) throws SAMLException {
134        this.value = checkAndGetValue(value);
135    }
136
137    private String checkAndGetValue(String value) throws SAMLException {
138        if ((value == null) || (value.length() == 0)) {
139            SAMLUtils.debug.message("StatusCode: empty attribute Value.");
140            throw new SAMLRequesterException(
141                        SAMLUtils.bundle.getString("missingAttribute"));
142        }
143        
144        if (value.indexOf(":") == -1) {
145            return (SAMLConstants.PROTOCOL_PREFIX + value);
146        } else {
147            StringTokenizer st = new StringTokenizer(value, ":");
148            if (st.countTokens() != 2) {
149                SAMLUtils.debug.message("StatusCode: wrong attribute value.");
150                throw new SAMLRequesterException(
151                        SAMLUtils.bundle.getString("wrongAttrValue"));
152            }
153            return value;
154        }
155    }
156
157    /**
158     * Sets the sub <code>StatusCode</code>.
159     * @param subcode <code>StatusCode</code> to be included.
160     */
161    public void setStatusCode(StatusCode subcode) {
162        subStatusCode = subcode;
163    }
164
165    /**
166     * Gets the sub <code>StatusCode</code> of the <code>StatusCode</code>.
167     * @return <code>StatusCode</code>.
168     */
169    public StatusCode getStatusCode() {
170        return subStatusCode;
171    }
172
173    /**
174     * Gets the value of the <code>StatusCode</code>.
175     * @return A String representing the value of the <code>StatusCode</code>.
176     */
177    public String getValue() {
178        return value;
179    }
180
181    /**
182     * Translates the <code>StatusCode</code> to an XML document String
183     * based on the SAML schema.
184     * @return An XML String representing the <code>StatusCode</code>.
185     */
186    public String toString() {
187        return toString(true, false);
188    }
189
190    /**
191     * Creates a String representation of the
192     * <code>&lt;samlp:StatusCode&gt;</code> element.
193     *
194     * @param includeNS Determines whether or not the namespace qualifier
195     *        is prepended to the Element when converted
196     * @param declareNS Determines whether or not the namespace is declared
197     *        within the Element.
198     * @return A string containing the valid XML for this element.
199     */   
200    public String toString(boolean includeNS, boolean declareNS) {
201        StringBuffer xml = new StringBuffer(100);
202        String prefix = "";
203        String uri = "";
204        if (includeNS) {
205            prefix = SAMLConstants.PROTOCOL_PREFIX;
206        }
207        if (declareNS) {
208            uri = SAMLConstants.PROTOCOL_NAMESPACE_STRING;
209        }
210        String tag = "StatusCode";
211        xml.append("<").append(prefix).append(tag).append(uri).
212            append(" Value=\"");
213        if (value.startsWith(SAMLConstants.PROTOCOL_PREFIX)) {
214            xml.append(value);
215        } else {
216            try {
217                xml.append(checkAndGetValue(value));
218            } catch (SAMLException e) {
219                SAMLUtils.debug.error("StatusCode.toString: ", e);
220                xml.append(value);
221            }
222        }
223        xml.append("\">\n");
224        if ((subStatusCode != null) &&
225            (subStatusCode != Collections.EMPTY_LIST)) {
226            xml.append(subStatusCode.toString(includeNS, false));
227        }
228        xml.append("</").append(prefix).append(tag).append(">\n");
229        return xml.toString();
230    }
231}