001    /*
002     * Copyright 2002 - 2009 JEuclid, http://jeuclid.sf.net
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    /* $Id: Mfenced.java,v 34009469c57a 2009/08/07 13:21:37 max $ */
018    
019    package net.sourceforge.jeuclid.elements.presentation.general;
020    
021    import java.util.ArrayList;
022    import java.util.List;
023    
024    import net.sourceforge.jeuclid.Constants;
025    import net.sourceforge.jeuclid.elements.AbstractElementWithDelegates;
026    import net.sourceforge.jeuclid.elements.AbstractJEuclidElement;
027    import net.sourceforge.jeuclid.elements.presentation.token.Mo;
028    import net.sourceforge.jeuclid.elements.support.operatordict.OperatorDictionary;
029    import net.sourceforge.jeuclid.layout.LayoutableNode;
030    
031    import org.apache.batik.dom.AbstractDocument;
032    import org.w3c.dom.Node;
033    import org.w3c.dom.mathml.MathMLFencedElement;
034    
035    /**
036     * The class represents the mfenced element.
037     * 
038     * @version $Revision: 34009469c57a $
039     */
040    public final class Mfenced extends AbstractElementWithDelegates implements
041            MathMLFencedElement {
042    
043        /** The separators attribute. */
044        public static final String ATTR_SEPARATORS = "separators";
045    
046        /** The close attribute. */
047        public static final String ATTR_CLOSE = "close";
048    
049        /** The open attribute. */
050        public static final String ATTR_OPEN = "open";
051    
052        /**
053         * The XML element from this class.
054         */
055        public static final String ELEMENT = "mfenced";
056    
057        private static final String FENCE_SPACE = "0.2em";
058    
059        private static final long serialVersionUID = 1L;
060    
061        /**
062         * Default constructor. Sets MathML Namespace.
063         * 
064         * @param qname
065         *            Qualified name.
066         * @param odoc
067         *            Owner Document.
068         */
069        public Mfenced(final String qname, final AbstractDocument odoc) {
070            super(qname, odoc);
071            this.setDefaultMathAttribute(Mfenced.ATTR_OPEN, "(");
072            this.setDefaultMathAttribute(Mfenced.ATTR_CLOSE, ")");
073            this.setDefaultMathAttribute(Mfenced.ATTR_SEPARATORS, ",");
074        }
075    
076        /** {@inheritDoc} */
077        @Override
078        protected Node newNode() {
079            return new Mfenced(this.nodeName, this.ownerDocument);
080        }
081    
082        /**
083         * @return opening delimiter
084         */
085        public String getOpen() {
086            return this.getMathAttribute(Mfenced.ATTR_OPEN);
087        }
088    
089        /**
090         * Set the opening delimiter.
091         * 
092         * @param open
093         *            Delimiter
094         */
095        public void setOpen(final String open) {
096            this.setAttribute(Mfenced.ATTR_OPEN, open);
097        }
098    
099        /**
100         * @return Return the closing delimiter
101         */
102        public String getClose() {
103            return this.getMathAttribute(Mfenced.ATTR_CLOSE);
104        }
105    
106        /**
107         * Set the closing delimiter.
108         * 
109         * @param close
110         *            New close delimeter
111         */
112        public void setClose(final String close) {
113            this.setAttribute(Mfenced.ATTR_CLOSE, close);
114        }
115    
116        /**
117         * Return the separators.
118         * 
119         * @return separators
120         */
121        public String getSeparators() {
122            final StringBuilder retVal = new StringBuilder();
123            final String attValue = this.getMathAttribute(Mfenced.ATTR_SEPARATORS);
124            if (attValue != null) {
125                for (int i = 0; i < attValue.length(); i++) {
126                    final char c = attValue.charAt(i);
127                    if (c > AbstractJEuclidElement.TRIVIAL_SPACE_MAX) {
128                        retVal.append(c);
129                    }
130                }
131            }
132            return retVal.toString();
133        }
134    
135        /**
136         * Set the separators.
137         * 
138         * @param separators
139         *            New separators
140         */
141        public void setSeparators(final String separators) {
142            this.setAttribute(Mfenced.ATTR_SEPARATORS, separators);
143        }
144    
145        /** {@inheritDoc} */
146        @Override
147        protected List<LayoutableNode> createDelegates() {
148            final int contentCount = this.getMathElementCount();
149            final List<LayoutableNode> retVal = new ArrayList<LayoutableNode>(
150                    2 * contentCount + 1);
151    
152            final Mo opOpen = this.createFenceOperator();
153            opOpen.setForm(OperatorDictionary.FORM_PREFIX);
154            opOpen.setTextContent(this.getOpen());
155    
156            retVal.add(opOpen);
157            final String sep = this.getSeparators();
158            final boolean haveSep = (sep != null) && (sep.length() > 0);
159    
160            for (int i = 0; i < contentCount; i++) {
161                retVal.add(this.getMathElement(i));
162    
163                if (haveSep && (i < (contentCount - 1))) {
164                    final Mo opSep = (Mo) this.getOwnerDocument().createElement(
165                            Mo.ELEMENT);
166                    opSep.setSeparator(Constants.TRUE);
167                    if (i < sep.length()) {
168                        opSep.setTextContent(String.valueOf(sep.charAt(i)));
169                    } else {
170                        opSep.setTextContent(String.valueOf(sep
171                                .charAt(sep.length() - 1)));
172                    }
173                    retVal.add(opSep);
174                }
175            }
176            final Mo opClose = this.createFenceOperator();
177            opClose.setForm(OperatorDictionary.FORM_POSTFIX);
178            opClose.setTextContent(this.getClose());
179            retVal.add(opClose);
180    
181            return retVal;
182        }
183    
184        private Mo createFenceOperator() {
185            final Mo opOpen = (Mo) this.getOwnerDocument()
186                    .createElement(Mo.ELEMENT);
187            opOpen.setFence(Constants.TRUE);
188            opOpen.setStretchy(Constants.TRUE);
189            opOpen.setRspace(Mfenced.FENCE_SPACE);
190            opOpen.setLspace(Mfenced.FENCE_SPACE);
191            opOpen.setSymmetric(Constants.FALSE);
192            return opOpen;
193        }
194    
195    }