001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.citation.annotation.output;
031
032import java.io.IOException;
033import java.io.Reader;
034import java.io.StringReader;
035import java.util.Collection;
036import java.util.List;
037
038import org.jbibtex.LaTeXObject;
039import org.jbibtex.LaTeXParser;
040import org.jbibtex.LaTeXPrinter;
041import org.jbibtex.ParseException;
042import org.openimaj.citation.annotation.Reference;
043
044/**
045 * Standard reference formatters.
046 * 
047 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
048 */
049public enum StandardFormatters implements ReferenceFormatter {
050        /**
051         * Format references as BibTeX.
052         * 
053         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
054         */
055        BIBTEX {
056                @Override
057                protected String formatRefs(Iterable<Reference> refs) {
058                        final StringBuilder builder = new StringBuilder();
059
060                        for (final Reference r : refs)
061                                builder.append(format(r));
062
063                        return builder.toString();
064                }
065
066                void appendNames(StringBuilder builder, String key, String[] authors) {
067                        if (authors == null || authors.length == 0 || (authors.length == 1 && authors[0].length() == 0))
068                                return;
069
070                        builder.append(" " + key + " = {");
071                        for (int i = 0; i < authors.length - 1; i++) {
072                                builder.append("{" + authors[i] + "} and ");
073                        }
074                        builder.append("{" + authors[authors.length - 1] + "}");
075                        builder.append("}\n");
076                }
077
078                @Override
079                public String format(Reference ref) {
080                        final StringBuilder builder = new StringBuilder();
081
082                        final String key = ref.hashCode() + "";
083                        builder.append("@" + ref.type() + "{" + key + "\n");
084                        appendNames(builder, "author", ref.author());
085
086                        builder.append(" title = {" + ref.title() + "}\n");
087                        builder.append(" year = {" + ref.year() + "}\n");
088
089                        if (ref.journal().length() > 0)
090                                builder.append(" journal = {" + ref.journal() + "}\n");
091                        if (ref.booktitle().length() > 0)
092                                builder.append(" booktitle = {" + ref.booktitle() + "}\n");
093                        if (ref.pages().length > 0) {
094                                if (ref.pages().length == 1)
095                                        builder.append(" pages = {" + ref.pages()[0] + "}\n");
096                                else if (ref.pages().length == 2)
097                                        builder.append(" pages = {" + ref.pages()[0] + "--" + ref.pages()[1] + "}\n");
098                                else {
099                                        builder.append(" pages = {");
100                                        for (int i = 0; i < ref.pages().length - 1; i++)
101                                                builder.append(ref.pages()[i] + ", ");
102                                        builder.append(ref.pages()[ref.pages().length - 1] + "}\n");
103                                }
104                        }
105
106                        if (ref.chapter().length() > 0)
107                                builder.append(" chapter = {" + ref.chapter() + "}\n");
108                        if (ref.edition().length() > 0)
109                                builder.append(" edition = {" + ref.edition() + "}\n");
110                        if (ref.url().length() > 0)
111                                builder.append(" url = {" + ref.url() + "}\n");
112                        if (ref.note().length() > 0)
113                                builder.append(" note = {" + ref.note() + "}\n");
114
115                        appendNames(builder, "editor", ref.editor());
116
117                        if (ref.institution().length() > 0)
118                                builder.append(" institution = {" + ref.institution() + "}\n");
119                        if (ref.month().length() > 0)
120                                builder.append(" month = {" + ref.month() + "}\n");
121                        if (ref.number() != "")
122                                builder.append(" number = {" + ref.number() + "}\n");
123                        if (ref.organization().length() > 0)
124                                builder.append(" organization = {" + ref.organization() + "}\n");
125                        if (ref.publisher().length() > 0)
126                                builder.append(" publisher = {" + ref.publisher() + "}\n");
127                        if (ref.school().length() > 0)
128                                builder.append(" school = {" + ref.school() + "}\n");
129                        if (ref.series().length() > 0)
130                                builder.append(" series = {" + ref.series() + "}\n");
131                        if (ref.volume() != "")
132                                builder.append(" volume = {" + ref.volume() + "}\n");
133
134                        if (ref.customData().length > 1) {
135                                for (int i = 0; i < ref.customData().length; i += 2) {
136                                        builder.append(" " + ref.customData()[i] + " = {" + ref.customData()[i + 1] + "}\n");
137                                }
138                        }
139
140                        builder.append("}\n");
141
142                        return builder.toString();
143                }
144        },
145        /**
146         * Format as a {@link Reference} annotation for inclusion in code
147         * 
148         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
149         * 
150         */
151        REFERENCE_ANNOTATION {
152                @Override
153                protected String formatRefs(Iterable<Reference> refs) {
154                        final StringBuilder builder = new StringBuilder();
155
156                        for (final Reference r : refs)
157                                builder.append(format(r));
158
159                        return builder.toString();
160                }
161
162                private String formatArray(String[] arr) {
163                        final StringBuilder builder = new StringBuilder();
164                        builder.append("{ ");
165                        for (int i = 0; i < arr.length - 1; i++) {
166                                builder.append(formatString(arr[i]) + ", ");
167                        }
168                        builder.append(formatString(arr[arr.length - 1]) + " }");
169
170                        return builder.toString();
171                }
172
173                private String formatString(String str) {
174                        return "\"" + str + "\"";
175                }
176
177                @Override
178                public String format(Reference ref) {
179                        final StringBuilder builder = new StringBuilder();
180
181                        builder.append("@Reference(\n");
182                        builder.append("\ttype = ReferenceType." + ref.type() + ",\n");
183                        builder.append("\tauthor = " + formatArray(ref.author()) + ",\n");
184                        builder.append("\ttitle = " + formatString(ref.title()) + ",\n");
185                        builder.append("\tyear = " + formatString(ref.year()) + ",\n");
186
187                        if (ref.journal().length() > 0)
188                                builder.append("\tjournal = " + formatString(ref.journal()) + ",\n");
189                        if (ref.booktitle().length() > 0)
190                                builder.append("\tbooktitle = " + formatString(ref.booktitle()) + ",\n");
191                        if (ref.pages().length > 0) {
192                                builder.append("\tpages = " + formatArray(ref.pages()) + ",\n");
193                        }
194
195                        if (ref.chapter().length() > 0)
196                                builder.append("\tchapter = " + formatString(ref.chapter()) + ",\n");
197                        if (ref.edition().length() > 0)
198                                builder.append("\tedition = " + formatString(ref.edition()) + ",\n");
199                        if (ref.url().length() > 0)
200                                builder.append("\turl = " + formatString(ref.url()) + ",\n");
201                        if (ref.note().length() > 0)
202                                builder.append("\tnote = " + formatString(ref.note()) + ",\n");
203                        if (ref.editor().length > 0)
204                                builder.append("\teditor = " + formatArray(ref.editor()) + ",\n");
205                        if (ref.institution().length() > 0)
206                                builder.append("\tinstitution = " + formatString(ref.institution()) + ",\n");
207                        if (ref.month().length() > 0)
208                                builder.append("\tmonth = " + formatString(ref.month()) + ",\n");
209                        if (ref.number() != "")
210                                builder.append("\tnumber = " + formatString(ref.number()) + ",\n");
211                        if (ref.organization().length() > 0)
212                                builder.append("\torganization = " + formatString(ref.organization()) + ",\n");
213                        if (ref.publisher().length() > 0)
214                                builder.append("\tpublisher = " + formatString(ref.publisher()) + ",\n");
215                        if (ref.school().length() > 0)
216                                builder.append("\tschool = " + formatString(ref.school()) + ",\n");
217                        if (ref.series().length() > 0)
218                                builder.append("\tseries = " + formatString(ref.series()) + ",\n");
219                        if (ref.volume() != "")
220                                builder.append("\tvolume = " + formatString(ref.volume()) + ",\n");
221
222                        if (ref.customData().length > 1) {
223                                builder.append("\tcustomData = {\n");
224                                for (int i = 0; i < ref.customData().length; i += 2) {
225                                        builder.append("\t\t" + formatString(ref.customData()[i]) + ", "
226                                                        + formatString(ref.customData()[i + 1]));
227                                        if (i < ref.customData().length - 2)
228                                                builder.append(",");
229                                        builder.append("\n");
230                                }
231                                builder.append("\t}\n");
232                        }
233
234                        if (builder.charAt(builder.length() - 2) == ',') {
235                                builder.deleteCharAt(builder.length() - 2);
236                        }
237                        builder.append(")\n");
238
239                        return builder.toString();
240                }
241
242        },
243        /**
244         * Format as a pretty string
245         * 
246         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
247         */
248        STRING {
249                @Override
250                protected String formatRefs(Iterable<Reference> refs) {
251                        final StringBuilder builder = new StringBuilder();
252
253                        for (final Reference r : refs)
254                                builder.append(format(r) + "\n");
255
256                        return builder.toString();
257                }
258
259                void appendNames(StringBuilder builder, String[] authors) {
260                        if (authors == null || authors.length == 0 || (authors.length == 1 && authors[0].length() == 0))
261                                return;
262
263                        if (authors.length == 1) {
264                                builder.append(formatName(authors[0]) + ".");
265                                return;
266                        }
267
268                        for (int i = 0; i < authors.length - 2; i++) {
269                                builder.append(formatName(authors[i]) + ", ");
270                        }
271                        builder.append(formatName(authors[authors.length - 2]) + " and " + formatName(authors[authors.length - 1])
272                                        + ". ");
273                }
274
275                String formatName(String name) {
276                        name = format(name);
277
278                        if (name.contains(",")) {
279                                final String lastName = name.substring(0, name.indexOf(","));
280                                final String[] firstNames = name.substring(name.indexOf(",") + 1).split(" ");
281
282                                String formatted = "";
283                                for (String f : firstNames) {
284                                        f = f.trim();
285                                        if (f.length() > 0)
286                                                formatted += f.charAt(0) + ". ";
287                                }
288
289                                return formatted + lastName;
290                        } else {
291                                final String[] parts = name.split(" ");
292                                String formatted = "";
293
294                                for (int i = 0; i < parts.length - 1; i++) {
295                                        formatted += parts[i].charAt(0) + ". ";
296                                }
297
298                                return formatted + parts[parts.length - 1];
299                        }
300                }
301
302                @Override
303                public String format(Reference ref) {
304                        final StringBuilder builder = new StringBuilder();
305
306                        appendNames(builder, ref.author());
307
308                        builder.append(format(ref.title()) + ". ");
309
310                        if (ref.journal().length() > 0)
311                                builder.append(format(ref.journal()) + ". ");
312                        if (ref.booktitle().length() > 0)
313                                builder.append(format(ref.booktitle()) + ". ");
314                        if (ref.institution().length() > 0)
315                                builder.append(format(ref.institution()) + ". ");
316                        if (ref.school().length() > 0)
317                                builder.append(format(ref.school()) + ". ");
318                        if (ref.publisher().length() > 0)
319                                builder.append(format(ref.publisher()) + ". ");
320                        if (ref.organization().length() > 0)
321                                builder.append(format(ref.organization()) + ". ");
322
323                        if (ref.pages().length > 0) {
324                                if (ref.pages().length == 1)
325                                        builder.append("p" + ref.pages()[0] + ". ");
326                                else if (ref.pages().length == 2)
327                                        builder.append("pp" + ref.pages()[0] + "-" + ref.pages()[1] + ". ");
328                                else {
329                                        builder.append("pp");
330                                        for (int i = 0; i < ref.pages().length - 1; i++)
331                                                builder.append(ref.pages()[i] + ", ");
332                                        builder.append(ref.pages()[ref.pages().length - 1] + ". ");
333                                }
334                        }
335
336                        if (ref.month().length() > 0)
337                                builder.append(format(ref.month()) + ", ");
338                        builder.append(format(ref.year()) + ". ");
339
340                        if (ref.url().length() > 0)
341                                builder.append(format(ref.url()));
342
343                        return builder.toString();
344                }
345        },
346        /**
347         * Format as an HTML fragment
348         * 
349         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
350         */
351        HTML {
352                @Override
353                protected String formatRefs(Iterable<Reference> refs) {
354                        final StringBuilder builder = new StringBuilder();
355
356                        for (final Reference r : refs) {
357                                builder.append("<p class=\"ref\">");
358                                builder.append(format(r));
359                                builder.append("</p>\n");
360                        }
361
362                        return builder.toString();
363                }
364
365                void appendNames(StringBuilder builder, String[] authors) {
366                        if (authors == null || authors.length == 0 || (authors.length == 1 && authors[0].length() == 0))
367                                return;
368
369                        if (authors.length == 1) {
370                                builder.append(formatName(authors[0]) + ". ");
371                                return;
372                        }
373
374                        for (int i = 0; i < authors.length - 2; i++) {
375                                builder.append(formatName(authors[i]) + ", ");
376                        }
377                        builder.append(formatName(authors[authors.length - 2]) + " and " + formatName(authors[authors.length - 1])
378                                        + ". ");
379                }
380
381                String formatName(String name) {
382                        name = format(name);
383
384                        if (name.contains(",")) {
385                                final String lastName = name.substring(0, name.indexOf(","));
386                                final String[] firstNames = name.substring(name.indexOf(",") + 1).split(" ");
387
388                                String formatted = "";
389                                for (String f : firstNames) {
390                                        f = f.trim();
391                                        if (f.length() > 0)
392                                                formatted += f.charAt(0) + ". ";
393                                }
394
395                                return formatted + lastName;
396                        } else {
397                                final String[] parts = name.split(" ");
398                                String formatted = "";
399
400                                for (int i = 0; i < parts.length - 1; i++) {
401                                        formatted += parts[i].charAt(0) + ". ";
402                                }
403
404                                return formatted + parts[parts.length - 1];
405                        }
406                }
407
408                @Override
409                public String format(Reference ref) {
410                        final StringBuilder builder = new StringBuilder();
411
412                        builder.append("<span class='authors'>");
413                        appendNames(builder, ref.author());
414                        builder.append("</span>");
415
416                        builder.append("<span class='title'>");
417                        builder.append(format(ref.title()) + ". ");
418                        builder.append("</span>");
419
420                        if (ref.journal().length() > 0)
421                                builder.append("<span class='journal'>" + format(ref.journal()) + ". </span>");
422                        if (ref.booktitle().length() > 0)
423                                builder.append("<span class='booktitle'>" + format(ref.booktitle()) + ". </span>");
424                        if (ref.institution().length() > 0)
425                                builder.append("<span class='institution'>" + format(ref.institution()) + ". </span>");
426                        if (ref.school().length() > 0)
427                                builder.append("<span class='school'>" + format(ref.school()) + ". </span>");
428                        if (ref.publisher().length() > 0)
429                                builder.append("<span class='publisher'>" + format(ref.publisher()) + ". </span>");
430                        if (ref.organization().length() > 0)
431                                builder.append("<span class='organization'>" + format(ref.organization()) + ". </span>");
432
433                        if (ref.pages().length > 0) {
434                                if (ref.pages().length == 1)
435                                        builder.append("<span class='pages'>" + "p" + ref.pages()[0] + ". </span>");
436                                else if (ref.pages().length == 2)
437                                        builder.append("<span class='pages'>" + "pp" + ref.pages()[0] + "-" + ref.pages()[1] + ". </span>");
438                                else {
439                                        builder.append("<span class='pages'>" + "pp");
440                                        for (int i = 0; i < ref.pages().length - 1; i++)
441                                                builder.append(ref.pages()[i] + ", ");
442                                        builder.append(ref.pages()[ref.pages().length - 1] + ". </span>");
443                                }
444                        }
445
446                        if (ref.month().length() > 0)
447                                builder.append("<span class='month'>" + format(ref.month()) + ", </span>");
448                        builder.append("<span class='year'>" + format(ref.year()) + ". </span>");
449
450                        if (ref.url().length() > 0)
451                                builder.append("<a class='url' href='" + format(ref.url()) + "'>" + format(ref.url()) + "</a>");
452
453                        return builder.toString();
454                }
455        };
456
457        protected abstract String formatRefs(Iterable<Reference> refs);
458
459        @Override
460        public abstract String format(Reference ref);
461
462        @Override
463        public String format(Collection<Reference> refs) {
464                return formatRefs(refs);
465        }
466
467        static List<LaTeXObject> parseLaTeX(String string) throws IOException, ParseException {
468                final Reader reader = new StringReader(string);
469
470                try {
471                        final LaTeXParser parser = new LaTeXParser();
472
473                        return parser.parse(reader);
474                } finally {
475                        reader.close();
476                }
477        }
478
479        static String printLaTeX(List<LaTeXObject> objects) {
480                final LaTeXPrinter printer = new LaTeXPrinter();
481
482                return printer.print(objects);
483        }
484
485        static String format(String latex) {
486                try {
487                        return printLaTeX(parseLaTeX(latex));
488                } catch (final Exception e) {
489                        return latex;
490                }
491        }
492}