001    /*
002     * Copyright 2002 - 2007 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: Menclose.java 310 2007-05-18 20:26:36Z maxberger $ */
018    
019    package net.sourceforge.jeuclid.elements.presentation.general;
020    
021    import java.awt.BasicStroke;
022    import java.awt.Graphics2D;
023    import java.awt.Stroke;
024    import java.awt.geom.Line2D;
025    import java.lang.reflect.Constructor;
026    import java.lang.reflect.InvocationTargetException;
027    import java.util.HashMap;
028    import java.util.List;
029    import java.util.Locale;
030    import java.util.Map;
031    import java.util.Stack;
032    import java.util.Vector;
033    
034    import net.sourceforge.jeuclid.MathBase;
035    import net.sourceforge.jeuclid.elements.AbstractElementWithDelegates;
036    import net.sourceforge.jeuclid.elements.JEuclidElement;
037    import net.sourceforge.jeuclid.elements.presentation.token.Mspace;
038    import net.sourceforge.jeuclid.elements.support.ElementListSupport;
039    import net.sourceforge.jeuclid.elements.support.GraphicsSupport;
040    
041    import org.apache.commons.logging.Log;
042    import org.apache.commons.logging.LogFactory;
043    import org.w3c.dom.mathml.MathMLEncloseElement;
044    
045    /**
046     * Class for supporting "menclose" elements.
047     * 
048     * @author Max Berger
049     * @version $Revision: 310 $
050     */
051    public class Menclose extends AbstractElementWithDelegates implements
052            MathMLEncloseElement {
053    
054        /**
055         * Char for rendering left part of the long division.
056         */
057        public static final char LONGDIV_CHAR = ')';
058    
059        /**
060         * Represents the US long-division notation, to support the notation
061         * "longdiv".
062         * 
063         * @author Max Berger
064         */
065        public static class Longdiv extends AbstractRoot {
066            /**
067             * Default constructor.
068             * 
069             * @param base
070             *            MathBase to use.
071             */
072            public Longdiv(final MathBase base) {
073                super(base, Menclose.LONGDIV_CHAR);
074            }
075    
076            /** {@inheritDoc} */
077            @Override
078            protected JEuclidElement getActualIndex() {
079                return new Mspace(this.getMathBase());
080            }
081    
082            /** {@inheritDoc} */
083            @Override
084            protected List<JEuclidElement> getContent() {
085                return ElementListSupport.createListOfChildren(this);
086            }
087    
088            /** {@inheritDoc} */
089            public String getTagName() {
090                return "";
091            }
092    
093        }
094    
095        /**
096         * base class for all row-like notations.
097         * 
098         * @author Max Berger
099         */
100        public abstract static class AbstractRowLikeNotation extends
101                AbstractRowLike {
102    
103            /**
104             * Default constructor.
105             * 
106             * @param base
107             *            MathBase to use.
108             */
109            public AbstractRowLikeNotation(final MathBase base) {
110                super(base);
111            }
112    
113            /** {@inheritDoc} */
114            public String getTagName() {
115                return "";
116            }
117        }
118    
119        /**
120         * base class for all row-like notations.
121         * 
122         * @author Max Berger
123         */
124        public static class Updiagonalstrike extends
125                Menclose.AbstractRowLikeNotation {
126    
127            /**
128             * Default constructor.
129             * 
130             * @param base
131             *            MathBase to use.
132             */
133            public Updiagonalstrike(final MathBase base) {
134                super(base);
135            }
136    
137            /** {@inheritDoc} */
138            @Override
139            public void paint(final Graphics2D g, final float posX,
140                    final float posY) {
141                super.paint(g, posX, posY);
142                final Stroke oldStroke = g.getStroke();
143                g.setStroke(new BasicStroke(GraphicsSupport.lineWidth(this)));
144                g.draw(new Line2D.Float(posX, posY, posX + this.getWidth(g), posY
145                        - this.getHeight(g)));
146                g.setStroke(oldStroke);
147            }
148        }
149    
150        /**
151         * base class for all row-like notations.
152         * 
153         * @author Max Berger
154         */
155        public static class Downdiagonalstrike extends
156                Menclose.AbstractRowLikeNotation {
157    
158            /**
159             * Default constructor.
160             * 
161             * @param base
162             *            MathBase to use.
163             */
164            public Downdiagonalstrike(final MathBase base) {
165                super(base);
166            }
167    
168            /** {@inheritDoc} */
169            @Override
170            public void paint(final Graphics2D g, final float posX,
171                    final float posY) {
172                super.paint(g, posX, posY);
173                final Stroke oldStroke = g.getStroke();
174                g.setStroke(new BasicStroke(GraphicsSupport.lineWidth(this)));
175                g.draw(new Line2D.Float(posX, posY - this.getHeight(g), posX
176                        + this.getWidth(g), posY));
177                g.setStroke(oldStroke);
178            }
179        }
180    
181        /**
182         * The XML element from this class.
183         */
184        public static final String ELEMENT = "menclose";
185    
186        /** The notation attribute. */
187        public static final String ATTR_NOTATION = "notation";
188    
189        /**
190         * Logger for this class
191         */
192        private static final Log LOGGER = LogFactory.getLog(Menclose.class);
193    
194        private static final Map<String, Constructor<?>> IMPL_CLASSES = new HashMap<String, Constructor<?>>();;
195    
196        /**
197         * Creates a math element.
198         * 
199         * @param base
200         *            The base for the math element tree.
201         */
202        public Menclose(final MathBase base) {
203            super(base);
204            this.setDefaultMathAttribute(Menclose.ATTR_NOTATION, "");
205        }
206    
207        /**
208         * @return notation of menclose element
209         */
210        public String getNotation() {
211            return this.getMathAttribute(Menclose.ATTR_NOTATION);
212        }
213    
214        /**
215         * Sets notation for menclose element.
216         * 
217         * @param notation
218         *            Notation
219         */
220        public void setNotation(final String notation) {
221            this.setAttribute(Menclose.ATTR_NOTATION, notation);
222        }
223    
224        /** {@inheritDoc} */
225        public String getTagName() {
226            return Menclose.ELEMENT;
227        }
228    
229        /** {@inheritDoc} */
230        @Override
231        protected List<JEuclidElement> createDelegates() {
232            final String[] notations = this.getNotation().split(" ");
233            final Stack<Constructor<?>> notationImpls = new Stack<Constructor<?>>();
234            for (final String curNotation : notations) {
235                final Constructor<?> con = Menclose.IMPL_CLASSES.get(curNotation
236                        .toLowerCase(Locale.ENGLISH));
237                if (con != null) {
238                    notationImpls.push(con);
239                } else if (curNotation.length() > 0) {
240                    Menclose.LOGGER.info("Unsupported notation for menclose: "
241                            + curNotation);
242                }
243            }
244            // This is just to make sure that there is at least one delegate, and
245            // that each of the standard delegates has exactly one child.
246            JEuclidElement lastChild;
247            if (this.getMathElementCount() != 1) {
248                lastChild = new Mrow(this.getMathBase());
249                for (final JEuclidElement child : ElementListSupport
250                        .createListOfChildren(this)) {
251                    lastChild.appendChild(child);
252                }
253            } else {
254                lastChild = this.getMathElement(0);
255            }
256            while (!notationImpls.isEmpty()) {
257                final Constructor<?> con = notationImpls.pop();
258                try {
259                    final JEuclidElement element = (JEuclidElement) con
260                            .newInstance(this.getMathBase());
261                    element.appendChild(lastChild);
262                    lastChild = element;
263                } catch (final InstantiationException e) {
264                    Menclose.LOGGER.warn(e);
265                } catch (final IllegalAccessException e) {
266                    Menclose.LOGGER.warn(e);
267                } catch (final InvocationTargetException e) {
268                    Menclose.LOGGER.warn(e);
269                }
270            }
271            final List<JEuclidElement> delegates = new Vector<JEuclidElement>(1);
272            delegates.add(lastChild);
273            return delegates;
274        }
275    
276        static {
277            try {
278                Menclose.IMPL_CLASSES.put("radical", Msqrt.class
279                        .getConstructor(MathBase.class));
280                Menclose.IMPL_CLASSES.put("longdiv", Menclose.Longdiv.class
281                        .getConstructor(MathBase.class));
282                Menclose.IMPL_CLASSES.put("updiagonalstrike",
283                        Menclose.Updiagonalstrike.class
284                                .getConstructor(MathBase.class));
285                Menclose.IMPL_CLASSES.put("downdiagonalstrike",
286                        Menclose.Downdiagonalstrike.class
287                                .getConstructor(MathBase.class));
288            } catch (NoSuchMethodException e) {
289                Menclose.LOGGER.fatal(e);
290            }
291        }
292    }