/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.generator; import java.io.IOException; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.openflexo.foundation.rm.FlexoProject; import org.openflexo.generator.exception.JavaFormattingException; import org.openflexo.logging.FlexoLogger; import org.openflexo.toolbox.StringUtils; import de.hunsicker.jalopy.Jalopy; import de.hunsicker.jalopy.storage.Convention; import de.hunsicker.jalopy.storage.ConventionKeys; /** * The class GeneratorFormatter was first created for the FlexoCodeGenerator project. * * @author gpolet * */ public class GeneratorFormatter { private static final Logger logger = FlexoLogger.getLogger(GeneratorFormatter.class.getPackage().getName()); private static final String MULTIPLE_NEW_LINE_ALONE_REG_EXP = "([\\s&&[^\n\r]]*[\n\r]{2,})"; private static final String HTML_OPEN = "<[^/!][^<>]*>"; private static final String HTML_OPEN_CLOSE = "<[^/!][^<>/]*/\\s*>"; private static final Pattern HTML_OPEN_PATTERN = Pattern.compile(HTML_OPEN, Pattern.DOTALL); private static final String HTML_CLOSE = "</[^>]*>"; private static final Pattern HTML_CLOSE_PATTERN = Pattern.compile(HTML_CLOSE, Pattern.DOTALL); private static final String WOD_HEADER = "\\s*[A-Za-z_0-9]+\\s*:\\s*[A-Za-z_0-9]+\\s*\\{\\s*"; private static final String WOD_FOOTER = "\\s*\\}\\s*"; private static long lastRead = -1; private static class JalopyFactory { private static Jalopy jalopy; public static Jalopy getJalopy() { if (jalopy == null) { jalopy = new Jalopy(); } jalopy.reset(); return jalopy; } } public synchronized static String formatJavaCode(String javaCode, String packageName, String className, CGGenerator generator, FlexoProject project) throws JavaFormattingException { if (javaCode == null) { return null; } if (javaCode.trim().equals("")) { return ""; } if (lastRead == -1 || lastRead != project.getJavaFormatterSettings().getFile().lastModified()) { if (lastRead != -1) { if (logger.isLoggable(Level.INFO)) { logger.info("Update detected on java formatter file"); } } try { Convention.importSettings(project.getJavaFormatterSettings().getFile()); lastRead = project.getJavaFormatterSettings().getFile().lastModified(); String sortOrder = Convention.getInstance().get(ConventionKeys.SORT_ORDER, null); if (sortOrder != null) { StringBuilder sb = new StringBuilder(); StringTokenizer st = new StringTokenizer(sortOrder, "|", false); while (st.hasMoreElements()) { String s = (String) st.nextElement(); if (s.equals("enum")) { if (sb.length() == 0 || sb.toString().equals("static")) { if (sb.length() != 0) { sb.append("|"); } sb.append(s); } else { sb.insert(0, "enum|"); } } else { if (sb.length() != 0) { sb.append("|"); } sb.append(s); } } if (!sb.toString().equals(sortOrder)) { Convention.getInstance().put(ConventionKeys.SORT_ORDER, sb.toString()); } } } catch (IOException e) { e.printStackTrace(); } } javaCode = javaCode.replace("\r", ""); // javaCode = javaCode.replaceAll(MULTIPLE_NEW_LINE_ALONE_REG_EXP, LINE_SEPARATOR+LINE_SEPARATOR); Jalopy jalopy = JalopyFactory.getJalopy(); try { StringBuffer sb = new StringBuffer(); jalopy.setInspect(false); jalopy.setForce(true); jalopy.setInput(javaCode, packageName.replace('.', '/') + className + ".java"); jalopy.setOutput(sb); if (jalopy.format()) {// This calls makes the format, but if the parse fails, then the stacktrace of it will be printed. return sb.toString(); } } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Exception occured while parsing the java code"); } } if (jalopy.getState() == Jalopy.State.ERROR) { logger.warning("Java code could not be formatted"); throw new JavaFormattingException(generator, packageName + "." + className); } if (logger.isLoggable(Level.WARNING)) { logger.warning("Java code could not be formatted: " + jalopy.getState()); } return javaCode; } public static String formatHTMLCode(String htmlCode) { if (htmlCode == null || htmlCode.trim().length() == 0) { return ""; } int indent = 0; int indexOpening = 0; int indexClosing = 0; int index = 0; int min = 0; Matcher open = HTML_OPEN_PATTERN.matcher(htmlCode); Matcher close = HTML_CLOSE_PATTERN.matcher(htmlCode); if (open.find()) { indexOpening = open.end(); } else { indexOpening = -1; } if (close.find()) { indexClosing = close.end(); } else { indexClosing = -1; } StringBuilder sb = new StringBuilder(); try { while ((indexOpening > -1 || indexClosing > -1) && index < htmlCode.length()) { if (indexOpening < 0 && indexClosing > 0) { if (htmlCode.substring(index, close.start()).trim().length() > 0) { sb.append(indentation(indent)); sb.append(htmlCode.substring(index, close.start()).trim()); sb.append(StringUtils.LINE_SEPARATOR); } indent--; sb.append(indentation(indent)); sb.append(htmlCode.substring(close.start(), indexClosing).trim()); sb.append(StringUtils.LINE_SEPARATOR); index = indexClosing; } else if (indexOpening > 0 && indexClosing < 0) { if (htmlCode.substring(index, open.start()).trim().length() > 0) { sb.append(indentation(indent)); sb.append(htmlCode.substring(index, open.start()).trim()); sb.append(StringUtils.LINE_SEPARATOR); } sb.append(indentation(indent)); if (tagRequiresIndentation(open.group())) { indent++; } sb.append(htmlCode.substring(open.start(), indexOpening).trim()); sb.append(StringUtils.LINE_SEPARATOR); index = indexOpening; } else if (indexOpening > 0 && indexClosing > 0) { min = Math.min(indexOpening, indexClosing); if (indexOpening == min) { if (htmlCode.substring(index, open.start()).trim().length() > 0) { sb.append(indentation(indent)); sb.append(htmlCode.substring(index, open.start()).trim()); sb.append(StringUtils.LINE_SEPARATOR); } sb.append(indentation(indent)); if (tagRequiresIndentation(open.group())) { indent++; } else { sb.deleteCharAt(sb.length() - 1); } sb.append(htmlCode.substring(open.start(), indexOpening).trim()); sb.append(StringUtils.LINE_SEPARATOR); index = indexOpening; } else { if (htmlCode.substring(index, close.start()).trim().length() > 0) { sb.append(indentation(indent)); sb.append(htmlCode.substring(index, close.start()).trim()); sb.append(StringUtils.LINE_SEPARATOR); } indent--; sb.append(indentation(indent)); sb.append(htmlCode.substring(close.start(), indexClosing).trim()); sb.append(StringUtils.LINE_SEPARATOR); index = indexClosing; } } else { sb.append(htmlCode.substring(index).trim()); index = htmlCode.length(); } if (index == indexOpening) { if (open.find()) { indexOpening = open.end(); } else { indexOpening = -1; } } else if (index == indexClosing) { if (close.find()) { indexClosing = close.end(); } else { indexClosing = -1; } } } } catch (RuntimeException re) { if (index > 0 && index < htmlCode.length()) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Failed to format at index " + index + " text: " + htmlCode.substring(index)); } } throw re; } return sb.toString(); } private static boolean tagRequiresIndentation(String tag) { return !tag.toLowerCase().equals("<br>") && !tag.toLowerCase().startsWith("<img") && !tag.matches(HTML_OPEN_CLOSE); } /** * @param indent * @return */ private static String indentation(int indent) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < indent; i++) { sb.append("\t"); } return sb.toString(); } public static String formatWodCode(String wodCode) { if (wodCode == null) { return null; } if (wodCode.trim().equals("")) { return ""; } wodCode = wodCode.replace("\r", ""); wodCode = wodCode.replaceAll(MULTIPLE_NEW_LINE_ALONE_REG_EXP, StringUtils.LINE_SEPARATOR + StringUtils.LINE_SEPARATOR); boolean insideHeaderFooter = false; if (wodCode.indexOf('\n') > -1) { StringTokenizer st = new StringTokenizer(wodCode.trim(), StringUtils.LINE_SEPARATOR); StringBuilder sb = new StringBuilder(); while (st.hasMoreTokens()) { String token = st.nextToken(); token = token.trim(); if (token.length() == 0) { continue; } if (token.matches(WOD_HEADER)) { sb.append(token.substring(0, token.indexOf(':')).trim()); sb.append(": "); sb.append(token.substring(token.indexOf(':') + 1, token.indexOf('{')).trim()); sb.append(" {" + StringUtils.LINE_SEPARATOR); insideHeaderFooter = true; } else if (token.matches(WOD_FOOTER)) { sb.append(token); sb.append(StringUtils.LINE_SEPARATOR + StringUtils.LINE_SEPARATOR); insideHeaderFooter = false; } else { if (insideHeaderFooter) { if (token.indexOf('=') > -1) { sb.append("\t").append(token.substring(0, token.indexOf('=')).trim()); sb.append(" = "); sb.append(token.substring(token.indexOf('=') + 1).trim()); } else { sb.append("\t").append(token); if (logger.isLoggable(Level.WARNING)) { logger.warning("Inside WOD Header footer but not equal (=) sign could be found: " + token); } } } else { if (logger.isLoggable(Level.WARNING)) { logger.warning("Not inside WOD Header footer: " + token); } sb.append(token); } sb.append(StringUtils.LINE_SEPARATOR); } } return sb.toString(); } else { return wodCode.trim() + StringUtils.LINE_SEPARATOR + StringUtils.LINE_SEPARATOR; } } public static void main(String[] args) { String s = "<HTML><BODY><BR><A HREF /><IMG><A ANCHOR=\"blabla\"/><A HREF / ></BODY></HTML>"; System.out.println(formatHTMLCode(s)); } }