001: // THIS SOFTWARE IS PROVIDED BY SOFTARIS PTY.LTD. AND OTHER METABOSS
002: // CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
003: // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
004: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTARIS PTY.LTD.
005: // OR OTHER METABOSS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
006: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
007: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
008: // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
009: // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
010: // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
011: // EVEN IF SOFTARIS PTY.LTD. OR OTHER METABOSS CONTRIBUTORS ARE ADVISED OF THE
012: // POSSIBILITY OF SUCH DAMAGE.
013: //
014: // Copyright 2000-2005 © Softaris Pty.Ltd. All Rights Reserved.
015: package com.metaboss.enterprise.xi.enhydrabarracuda.htmlwidgets;
016:
017: import java.util.ArrayList;
018: import java.util.HashMap;
019: import java.util.Iterator;
020: import java.util.List;
021: import java.util.Map;
022:
023: import org.w3c.dom.Document;
024: import org.w3c.dom.NodeList;
025: import org.w3c.dom.html.HTMLAnchorElement;
026: import org.w3c.dom.html.HTMLCollection;
027: import org.w3c.dom.html.HTMLTableCellElement;
028: import org.w3c.dom.html.HTMLTableElement;
029: import org.w3c.dom.html.HTMLTableRowElement;
030:
031: import com.metaboss.enterprise.ui.UIUnexpectedProgramConditionException;
032: import com.metaboss.enterprise.xi.enhydrabarracuda.Application;
033: import com.metaboss.enterprise.xi.enhydrabarracuda.Util;
034: import com.metaboss.enterprise.xi.enhydrabarracuda.Widget;
035: import com.metaboss.enterprise.xi.enhydrabarracuda.WidgetModel;
036:
037: /** This class is the implementation of the table using the SimpleTableModel of the TABLE element.
038: * It has following expectations from the proptotype:
039: * <UL>
040: * <LI>It expects to have one or more rows in the sample with all cells being TH cells. All of these must preceed
041: * the data cells.</LI>
042: * <LI>It expects to have one or more rows in the sample with all cells being TD cells. It will use
043: * these rows as "sample" rows - all of them will be available for use as prototype.</LI>
044: * </UL>
045: */
046: public class Table extends Widget {
047: /** This class represents the data model of the text control */
048: public static class Model implements WidgetModel {
049: public static class Cell {
050: private HTMLTableCellElement mSampleCellElement;
051: private String mText = null;
052:
053: private Cell(HTMLTableCellElement pSampleCellElement) {
054: mSampleCellElement = pSampleCellElement;
055: }
056:
057: /** Returns the value of the text to be set into the cell.
058: * @return the string set into the cell. If null is returned from here the cell will stay unmodified */
059: public String getText() {
060: return mText;
061: }
062:
063: /** Sets the value of the text to be set into the cell. */
064: public void setText(String pText) {
065: mText = pText;
066: }
067:
068: private HTMLTableCellElement getCellElement(
069: Document pDocument) {
070: HTMLTableCellElement lCellElement = (HTMLTableCellElement) pDocument
071: .importNode(mSampleCellElement, true);
072: // See if there is anything to do
073: if (mText != null) {
074: NodeList lSampleCellChildren = lCellElement
075: .getChildNodes();
076: int lSampleCellChildrenCount = lSampleCellChildren
077: .getLength();
078: for (int i = lSampleCellChildrenCount - 1; i >= 0; i--)
079: lCellElement.removeChild(lSampleCellChildren
080: .item(i));
081: lCellElement.appendChild(pDocument
082: .createTextNode(mText));
083: }
084: return lCellElement;
085: }
086: }
087:
088: public static class Row {
089: private Model mModel;
090: private HTMLTableRowElement mSampleRowElement;
091: private Cell[] mCells = null;
092: private Map mAnchorParameters = null;
093:
094: private Row(Model pModel, HTMLTableRowElement pSampleElement)
095: throws UIUnexpectedProgramConditionException {
096: mModel = pModel;
097: mSampleRowElement = pSampleElement;
098: HTMLCollection lCells = pSampleElement.getCells();
099: int lNumberOfCells = lCells.getLength();
100: if (lNumberOfCells < 1)
101: throw new UIUnexpectedProgramConditionException(
102: "Sample Row element must have at least one cell defined. The cells defined in the sample are used as a templates to build the real cells.");
103: mCells = new Cell[lNumberOfCells];
104: for (int i = 0; i < lNumberOfCells; i++)
105: mCells[i] = new Cell((HTMLTableCellElement) lCells
106: .item(i));
107: }
108:
109: /** Allows to set the parameter to be appended to all internal anchors found in this row.
110: * This is used to identify the source of navigation. */
111: public void setAnchorParameter(String pParameterName,
112: String pParameterValue) {
113: if (mAnchorParameters == null)
114: mAnchorParameters = new HashMap();
115: mAnchorParameters.put(pParameterName, pParameterValue);
116: }
117:
118: /** Returns the Cell object corresponding to the given column number */
119: public Cell getCell(int pCellIndex) {
120: return mCells[pCellIndex];
121: }
122:
123: private HTMLTableRowElement getRowElement(Document pDocument)
124: throws UIUnexpectedProgramConditionException {
125: HTMLTableRowElement lRowElement = (HTMLTableRowElement) pDocument
126: .importNode(mSampleRowElement, true);
127: // ----------- Start Work on inserting the columns
128: // First find the first sample cell, so we know where to insert new cells from
129: HTMLCollection lSampleCellsCollection = lRowElement
130: .getCells();
131: int lNumberOfSampleCells = lSampleCellsCollection
132: .getLength();
133: if (lNumberOfSampleCells != mCells.length)
134: throw new UIUnexpectedProgramConditionException(
135: "Data row element mismatch with the sample row (number of cells mismatch).");
136: HTMLTableCellElement lFirstSampleCell = (HTMLTableCellElement) lSampleCellsCollection
137: .item(0);
138: // Insert all real cells before this first sample cell
139: for (int i = 0; i < mCells.length; i++) {
140: lRowElement.insertBefore(mCells[i]
141: .getCellElement(pDocument),
142: lFirstSampleCell);
143: }
144: // Now delete all sample cells. Start from the back, so we do not have to adjust the index
145: for (int i = lNumberOfSampleCells - 1; i >= 0; i--)
146: lRowElement.deleteCell(mCells.length + i);
147: // ----------- End Work on inserting the columns
148:
149: // ----------- Start Work on anchor parameters
150: if (mAnchorParameters != null) {
151: NodeList lAnchors = lRowElement
152: .getElementsByTagName("A");
153: int lAnchorsCount = lAnchors.getLength();
154: for (int i = 0; i < lAnchorsCount; i++) {
155: HTMLAnchorElement lAnchor = (HTMLAnchorElement) lAnchors
156: .item(i);
157: String lHref = lAnchor.getHref();
158: if (mModel.mApplication.isOwnedUrl(lHref)) {
159: // This URL is owned by our application. Sign it.
160: for (Iterator lIter = mAnchorParameters
161: .entrySet().iterator(); lIter
162: .hasNext();) {
163: Map.Entry lEntry = (Map.Entry) lIter
164: .next();
165: lHref = Util.setURLParameter(lHref,
166: (String) lEntry.getKey(),
167: (String) lEntry.getValue());
168: }
169: lAnchor.setHref(lHref);
170: }
171: }
172: }
173: return lRowElement;
174: }
175: }
176:
177: private Application mApplication;
178: private HTMLTableElement mSampleTableElement;
179: private HTMLTableRowElement[] mSampleRows;
180: private List mRows = null;
181:
182: private Model(Application pApplication,
183: HTMLTableElement pSampleTableElement)
184: throws UIUnexpectedProgramConditionException {
185: mApplication = pApplication;
186: mSampleTableElement = pSampleTableElement;
187: // Collect all data rows and keep them as samples
188: // First find the beginning of the sample rows, so we know where to insert new rows from
189: HTMLCollection lRowsCollection = pSampleTableElement
190: .getRows();
191: int lNumberOfRows = lRowsCollection.getLength();
192: if (lNumberOfRows < 1)
193: throw new UIUnexpectedProgramConditionException(
194: "Sample Table element must have at least one row defined. The rows defined in the sample are used as a templates to build the real rows.");
195: List lSampleDataRows = new ArrayList();
196: for (int lRowIndex = 0; lRowIndex < lNumberOfRows; lRowIndex++) {
197: HTMLTableRowElement lRow = (HTMLTableRowElement) lRowsCollection
198: .item(lRowIndex);
199: if (isDataRow(lRow))
200: lSampleDataRows.add(lRow);
201: }
202: if (lSampleDataRows.size() == 0)
203: throw new UIUnexpectedProgramConditionException(
204: "Sample Table element must have at least one data row defined. The data rows defined in the sample are used as a templates to build the real rows.");
205: mSampleRows = (HTMLTableRowElement[]) lSampleDataRows
206: .toArray(new HTMLTableRowElement[lSampleDataRows
207: .size()]);
208: }
209:
210: /** Returns the aray of Rows to be set into the text widget */
211: public Row[] getRows() {
212: return mRows != null ? (Row[]) mRows.toArray(new Row[mRows
213: .size()]) : null;
214: }
215:
216: /** Adds the new row to the model based on the sample row with the given index. */
217: public Row addRow(int pSampeRowIndex)
218: throws UIUnexpectedProgramConditionException {
219: if (mRows == null)
220: mRows = new ArrayList();
221: Row lNewRow = new Row(this , mSampleRows[pSampeRowIndex]);
222: mRows.add(lNewRow);
223: return lNewRow;
224: }
225:
226: /** This method is used to apply the values passed from the form back into the model.
227: * @return the model object after the form values have been applied (The model object may just return itself)
228: * @exception UIUnexpectedProgramConditionException thrown when this widget model does not support editing */
229: public void applyFormValues(String[] pValues)
230: throws UIUnexpectedProgramConditionException {
231: throw new UIUnexpectedProgramConditionException(
232: "Table widget does not support editing and can not be used in the form");
233: }
234:
235: /** This method used primarily for debug purposes. It is expected to output a single string value reflecting the content of the model */
236: public String getDebugString() {
237: return "Not implemented yet";
238: }
239: }
240:
241: /** This standard method creates an instance of the model object for this widget */
242: public WidgetModel createModel()
243: throws UIUnexpectedProgramConditionException {
244: // Get the input element and create the model object initialised with it
245: HTMLTableElement lTableElement = (HTMLTableElement) getBoundDocumentElement();
246: Model lModel = new Model(getOwnerPage().getApplication(),
247: lTableElement);
248: return lModel;
249: }
250:
251: /** This lifecycle method is expected to render the control's image onto the document */
252: public void render() throws UIUnexpectedProgramConditionException {
253: Model lModel = (Model) getModel();
254: // There are data rows, we have to do something
255: HTMLTableElement lTableElement = (HTMLTableElement) getBoundDocumentElement();
256: // First find the beginning of the sample rows, so we know where to insert new rows from
257: HTMLCollection lSampleRowsCollection = lTableElement.getRows();
258: int lNumberOfSampleRows = lSampleRowsCollection.getLength();
259: if (lNumberOfSampleRows < 1)
260: throw new UIUnexpectedProgramConditionException(
261: "Sample Table element must have at least one row defined. The rows defined in the sample are used as a templates to build the real rows.");
262: int lFirstSampleDataRowIndex = 0;
263: HTMLTableRowElement lFirstSampleDataRow = null;
264: for (int lRowIndex = 0; lRowIndex < lNumberOfSampleRows; lRowIndex++) {
265: HTMLTableRowElement lRow = (HTMLTableRowElement) lSampleRowsCollection
266: .item(lRowIndex);
267: if (isDataRow(lRow)) {
268: lFirstSampleDataRowIndex = lRowIndex;
269: lFirstSampleDataRow = lRow;
270: break;
271: }
272: }
273: if (lFirstSampleDataRow == null)
274: throw new UIUnexpectedProgramConditionException(
275: "Sample Table element must have at least one data row defined. The data rows defined in the sample are used as a templates to build the real rows.");
276:
277: // Insert all real rows before this first sample row
278: Document lThisDocument = lTableElement.getOwnerDocument();
279: Model.Row[] lRows = lModel.getRows();
280: if (lRows != null && lRows.length > 0) {
281: for (int i = 0; i < lRows.length; i++) {
282: lTableElement.insertBefore(lRows[i]
283: .getRowElement(lThisDocument),
284: lFirstSampleDataRow);
285: }
286: // Now delete all sample data rows. Start from the back, so we do not have to adjust the index
287: for (int i = lNumberOfSampleRows - 1; i >= lFirstSampleDataRowIndex; i--)
288: lTableElement.deleteRow(lRows.length + i);
289: } else {
290: // Now delete all sample data rows. Start from the back, so we do not have to adjust the index
291: for (int i = lNumberOfSampleRows - 1; i >= lFirstSampleDataRowIndex; i--)
292: lTableElement.deleteRow(i);
293: }
294: }
295:
296: // Helper. Returns true if the given HTMLTableRowElement represents header row
297: private static boolean isHeaderRow(HTMLTableRowElement pRow)
298: throws UIUnexpectedProgramConditionException {
299: HTMLCollection lCells = pRow.getCells();
300: int lNumberOfCells = lCells.getLength();
301: if (lNumberOfCells < 1)
302: throw new UIUnexpectedProgramConditionException(
303: "Detected an empty Table Row (has no Cells).");
304: for (int i = 0; i < lNumberOfCells; i++) {
305: HTMLTableCellElement lCellElement = (HTMLTableCellElement) lCells
306: .item(i);
307: if (lCellElement.getTagName().equalsIgnoreCase("td"))
308: return false; // Dicovered at least one data cell
309: }
310: return true;
311: }
312:
313: // Helper. Returns true if the given HTMLTableRowElement represents data row
314: private static boolean isDataRow(HTMLTableRowElement pRow)
315: throws UIUnexpectedProgramConditionException {
316: HTMLCollection lCells = pRow.getCells();
317: int lNumberOfCells = lCells.getLength();
318: if (lNumberOfCells < 1)
319: throw new UIUnexpectedProgramConditionException(
320: "Detected an empty Table Row (has no Cells).");
321: for (int i = 0; i < lNumberOfCells; i++) {
322: HTMLTableCellElement lCellElement = (HTMLTableCellElement) lCells
323: .item(i);
324: if (lCellElement.getTagName().equalsIgnoreCase("th"))
325: return false; // Dicovered at least one header cell
326: }
327: return true;
328: }
329: }
|