001    /*
002     * Copyright 2007 - 2008 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: Mml2xxx.java,v 06749543c58f 2008/08/22 13:26:52 maxberger $ */
018    
019    package net.sourceforge.jeuclid.app;
020    
021    import java.io.File;
022    import java.io.IOException;
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    import java.util.List;
026    import java.util.Locale;
027    
028    import net.sourceforge.jeuclid.LayoutContext;
029    import net.sourceforge.jeuclid.MutableLayoutContext;
030    import net.sourceforge.jeuclid.context.LayoutContextImpl;
031    import net.sourceforge.jeuclid.context.Parameter;
032    import net.sourceforge.jeuclid.context.typewrapper.EnumTypeWrapper;
033    import net.sourceforge.jeuclid.context.typewrapper.TypeWrapper;
034    import net.sourceforge.jeuclid.converter.Converter;
035    import net.sourceforge.jeuclid.converter.ConverterRegistry;
036    
037    import org.apache.commons.cli.CommandLine;
038    import org.apache.commons.cli.GnuParser;
039    import org.apache.commons.cli.HelpFormatter;
040    import org.apache.commons.cli.Option;
041    import org.apache.commons.cli.Options;
042    import org.apache.commons.cli.ParseException;
043    import org.apache.commons.lang.StringUtils;
044    
045    /**
046     * Utility class to be used from the command line to call the converters.
047     * 
048     * @version $Revision: 06749543c58f $
049     */
050    // CHECKSTYLE:OFF
051    // Data abstraction coupling is too high. But it makes no sense to split up
052    // this class.
053    public final class Mml2xxx {
054        // CHECKSTYLE:ON
055    
056        private static final String OUT_FILE_TYPE = "outFileType";
057    
058        private static final String DEFAULT_TYPE = "image/png";
059    
060        private Mml2xxx() {
061            // Empty on purpose
062        }
063    
064        private static Options createOptions() {
065            final Options options = new Options();
066            final Option oft = new Option(Mml2xxx.OUT_FILE_TYPE, true,
067                    "output file mime type [default: derived from the target file's extention]"
068                            + "; available values are: "
069                            + StringUtils.join(ConverterRegistry.getInstance()
070                                    .getAvailableOutfileTypes().iterator(), ' '));
071            options.addOption(oft);
072            final LayoutContext defaultCtx = LayoutContextImpl
073                    .getDefaultLayoutContext();
074            for (final Parameter param : Parameter.values()) {
075                final TypeWrapper typeWrapper = param.getTypeWrapper();
076                final StringBuilder desc = new StringBuilder(param
077                        .getOptionDesc());
078                final String defValue = param.toString(defaultCtx
079                        .getParameter(param));
080                if (defValue != null) {
081                    desc.append(" [default: ").append(defValue).append("]");
082                }
083                final Option o = new Option(param.getOptionName(), true, desc
084                        .toString());
085                String argName = param.getTypeWrapper().getValueType()
086                        .getSimpleName().toLowerCase(Locale.ENGLISH);
087                if (typeWrapper instanceof EnumTypeWrapper) {
088                    argName = StringUtils.join(((EnumTypeWrapper) typeWrapper)
089                            .values(), '|');
090                }
091                o.setArgName(argName);
092                options.addOption(o);
093            }
094            return options;
095        }
096    
097        /**
098         * Main function for use from scripts.
099         * 
100         * @param args
101         *            command line arguments.
102         */
103        public static void main(final String[] args) {
104            final Options options = Mml2xxx.createOptions();
105            CommandLine cmdLine = null;
106            try {
107                cmdLine = new GnuParser().parse(options, args);
108    
109                final List<String> files = Arrays.asList(cmdLine.getArgs());
110                if (files.size() < 2) {
111                    throw new ParseException("Not enough arguments!");
112                }
113                final int sourceCount = files.size() - 1;
114                final File lastFile = new File(files.get(sourceCount));
115                final boolean multi = lastFile.isDirectory();
116                final List<File> sources = Mml2xxx.createListOfSourceFiles(files,
117                        sourceCount);
118    
119                final MutableLayoutContext ctx = Mml2xxx
120                        .createLayoutContext(cmdLine);
121                if (multi) {
122                    Mml2xxx.convertMultipleFiles(cmdLine, lastFile, sources, ctx);
123                } else {
124                    if (sources.size() != 1) {
125                        throw new ParseException(
126                                "Too many file arguments. Did you want to add a target directory?");
127                    }
128                    final String outFileType = Mml2xxx.findOutfileType(cmdLine,
129                            lastFile.getName());
130                    Converter.getInstance().convert(sources.get(0), lastFile,
131                            outFileType, ctx);
132                }
133            } catch (final ParseException pe) {
134                System.err.println(pe);
135                Mml2xxx.showUsage(options);
136                System.exit(1);
137            } catch (final IOException ioe) {
138                System.err.println("Error encountered during converion process");
139                ioe.printStackTrace(System.err);
140                System.exit(2);
141            } catch (final IllegalArgumentException iae) {
142                System.err.println(iae);
143                Mml2xxx.showUsage(options);
144                System.exit(1);
145            }
146        }
147    
148        private static void convertMultipleFiles(final CommandLine cmdLine,
149                final File lastFile, final List<File> sources,
150                final MutableLayoutContext layoutContext) throws ParseException,
151                IOException {
152            final String outFileType = Mml2xxx.findOutfileType(cmdLine, null);
153            for (final File source : sources) {
154                final String fileName = source.getName();
155                final int dotpos = fileName.lastIndexOf('.');
156                final String baseName;
157                if (dotpos >= 0) {
158                    baseName = fileName.substring(0, dotpos);
159                } else {
160                    baseName = fileName;
161                }
162                final File target = new File(lastFile, baseName
163                        + '.'
164                        + ConverterRegistry.getInstance().getSuffixForMimeType(
165                                outFileType));
166                Converter.getInstance().convert(source, target, outFileType,
167                        layoutContext);
168            }
169        }
170    
171        private static List<File> createListOfSourceFiles(
172                final List<String> files, final int count) throws ParseException {
173            final List<File> sources = new ArrayList<File>(count);
174            for (int i = 0; i < count; i++) {
175                final String current = files.get(i);
176                final File source = new File(current);
177                if (!source.isFile() || !source.canRead()) {
178                    throw new ParseException(current
179                            + " is not a file or not readable");
180                }
181                sources.add(source);
182            }
183            return sources;
184        }
185    
186        private static String findOutfileType(final CommandLine cmdLine,
187                final String fileName) throws ParseException {
188            String outFileType = cmdLine.getOptionValue(Mml2xxx.OUT_FILE_TYPE);
189            final String isNotSupported = " is not supported";
190            if ((outFileType == null) && (fileName != null)) {
191                final int dot = fileName.lastIndexOf('.');
192                if (dot != -1 && dot != fileName.length() - 1) {
193                    final String extension = fileName.substring(dot + 1);
194                    outFileType = ConverterRegistry.getInstance()
195                            .getMimeTypeForSuffix(extension);
196                }
197            }
198            if (outFileType == null) {
199                System.out.println("No ouput type could be detected, assuming "
200                        + Mml2xxx.DEFAULT_TYPE);
201                outFileType = Mml2xxx.DEFAULT_TYPE;
202            } else {
203                if (!ConverterRegistry.getInstance().getAvailableOutfileTypes()
204                        .contains(outFileType)) {
205                    throw new IllegalArgumentException("Output type "
206                            + outFileType + isNotSupported);
207                }
208            }
209            return outFileType;
210        }
211    
212        private static MutableLayoutContext createLayoutContext(
213                final CommandLine cmdLine) {
214            final MutableLayoutContext ctx = new LayoutContextImpl(
215                    LayoutContextImpl.getDefaultLayoutContext());
216            for (final Parameter param : Parameter.values()) {
217                final String value = cmdLine
218                        .getOptionValue(param.getOptionName());
219                if (value != null) {
220                    ctx.setParameter(param, param.fromString(value));
221                }
222            }
223            return ctx;
224        }
225    
226        private static void showUsage(final Options options) {
227            final HelpFormatter hf = new HelpFormatter();
228            final String lineSep = hf.getNewLine();
229            hf
230                    .printHelp(
231                            "mml2xxx <source file(s)> <target file/directory> [options]",
232                            "source is the path to the source file (MathML or ODF format)"
233                                    + lineSep
234                                    + "target is the path to the target file / directory"
235                                    + lineSep
236                                    + "If multiple source files are given, target must be a directory",
237                            options,
238                            "Example: mml2xxx a.mml a.png -backgroundColor white");
239        }
240    
241    }