package com.liferay.ide.velocity.vaulttec.ui.editor.actions; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.action.IAction; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentPartitioner; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.rules.FastPartitioner; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.widgets.Shell; import com.liferay.ide.velocity.editor.EditorsUtil; import com.liferay.ide.velocity.editor.compare.VelocityCompare; import com.liferay.ide.velocity.editor.compare.VelocityInput; import com.liferay.ide.velocity.scanner.VelocityPartitionScanner; import com.liferay.ide.velocity.ui.editor.xml.IEditorConfiguration; import com.liferay.ide.velocity.ui.editor.xml.VelocityAutoIndentStrategy; import com.liferay.ide.velocity.vaulttec.ui.VelocityPlugin; import com.liferay.ide.velocity.vaulttec.ui.editor.VelocityConfiguration; import com.liferay.ide.velocity.vaulttec.ui.model.Directive; public class Formatter { private Object _selected = null; public static final String LINE_SEP = System.getProperty("line.separator"); /** * @param document */ public IDocument format(final IDocument document) { Runnable r = new Runnable() { public void run() { String textBeforeFormatting = document.get(); IDocument tempDocument = new Document(); boolean isUppercaseEnabled = VelocityPlugin.getDefault().isUppercaseEnabled(); Pattern pattern = null; Matcher matcher = null; String newText = textBeforeFormatting; String patternStr = "^[\\s]*"; String replacementStr = ""; // put all the tags on newLine but not those which start // with ## pattern = Pattern.compile("<[/]?(?!textarea)[a-zA-Z]+"); matcher = pattern.matcher(newText); StringBuffer buf = new StringBuffer(); while ((matcher.find())) { // Get the match result String replaceStr = matcher.group(); // Insert replacement matcher.appendReplacement(buf, replaceStr); } matcher.appendTail(buf); // Get result newText = buf.toString(); String patternStrClose, replaceStrClose = ""; patternStrClose = "(</.*?>|<.*?/>)"; replaceStrClose = "$1\n"; pattern = Pattern.compile(patternStrClose, Pattern.MULTILINE); matcher = pattern.matcher(newText); newText = matcher.replaceAll(replaceStrClose); patternStrClose = "(<(?!/)[a-zA-Z].*?>)\\s*?(?=<(?!/)[a-zA-Z].*?>)"; replaceStrClose = "$1\n"; pattern = Pattern.compile(patternStrClose, Pattern.MULTILINE); matcher = pattern.matcher(newText); newText = matcher.replaceAll(replaceStrClose); pattern = Pattern.compile(patternStr, Pattern.MULTILINE); matcher = pattern.matcher(newText); newText = matcher.replaceAll(replacementStr); IDocumentPartitioner partitioner = new FastPartitioner(new VelocityPartitionScanner(), VelocityPartitionScanner.TYPES); partitioner.connect(tempDocument); tempDocument.setDocumentPartitioner(partitioner); tempDocument.set(newText); int numberOfLines = tempDocument.getNumberOfLines(); if (numberOfLines > 0) { for (int i = 0; i < numberOfLines; i++) { try { IRegion lineInformation = tempDocument.getLineInformation(i); int orginalOffset = lineInformation.getOffset(); int originalLength = lineInformation.getLength(); String oldText = tempDocument.get(orginalOffset, originalLength); String smartIndentOnTabResult = smartIndentOnTab(tempDocument, orginalOffset); tempDocument.replace(orginalOffset, originalLength, smartIndentOnTabResult + oldText); } catch (BadLocationException e) { System.out.println(e); } } } pattern = Pattern.compile("^[\t]|^[\\s]{4}", Pattern.MULTILINE); matcher = pattern.matcher(tempDocument.get()); newText = matcher.replaceAll(replacementStr); VelocityInput left = new VelocityInput("left", textBeforeFormatting); VelocityInput right = new VelocityInput("right", newText); VelocityCompare velocityCompare = new VelocityCompare(left, right, null); if (velocityCompare.openCompareDialog() == 0) { document.set(right.getText()); } } }; r.run(); return document; } public int getIndentOfLine(IDocument d, int line) throws BadLocationException { if (line >= 0) { int start = d.getLineOffset(line); int end = (start + d.getLineLength(line)) - 1; int whiteend = findEndOfWhiteSpace(d, start, end); return VelocityAutoIndentStrategy.indentWidthOf(d.get(start, whiteend - start), 4); } else { return 0; } } protected int findEndOfWhiteSpace(IDocument document, int offset, int end) throws BadLocationException { while (offset < end) { char c = document.getChar(offset); if (c != ' ' && c != '\t') { return offset; } offset++; } return end; } public boolean isInsideEscapedPartitions(int offset, IDocument doc) { if (EditorsUtil.isInsidePartition(offset, VelocityConfiguration.CDATA_PARTITIONS, true, doc)) { return true; } else { return EditorsUtil.isInsidePartition(offset, VelocityConfiguration.ESCAPED_PARTITIONS, doc); } } public int getNonBlankLineAbove(int line, IDocument doc) { IDocumentPartitioner partitioner = doc.getDocumentPartitioner(); while (--line >= 0) { if (EditorsUtil.isBlankLine(doc, line)) { continue; } try { int start = doc.getLineOffset(line); if (EditorsUtil.isInsidePartition(start, new String[] { IEditorConfiguration.CDATA_PARTITION }, true, doc)) { line = doc.getLineOfOffset(partitioner.getPartition(start).getOffset()); continue; } if (isInsideEscapedPartitions(start, doc)) { line = doc.getLineOfOffset(partitioner.getPartition(start).getOffset()) + 1; continue; } } catch (BadLocationException e) { } return line; } return line; } public int unIndentOnTagEnd(int indent, int offset, IDocument doc) { if (isEndTag(offset, doc) > 0) { return unIndent(indent, 1); } else { return indent; } } public int isEndTag(int offset, IDocument doc) { int len = doc.getLength(); if ((offset + 1) >= len || isInsideEscapedPartitions(offset, doc)) { return 0; } try { int i = offset; char c = '\0'; try { i = findEndOfWhiteSpace(doc, offset, offset + 20); c = doc.getChar(i); } catch (Exception e) { i = offset; } char cc = doc.getChar(i + 1); char ccc = '\0'; if ((offset + 2) < len) { ccc = doc.getChar(i + 2); } String f = VelocityAutoIndentStrategy.getVeloIdentifier(doc, i, i + 5); if (((c == '<') && (cc == '/')) || ((c == '/') && (cc == '>')) || ((c == '-') && (cc == '-') && (ccc == '>'))) { return i; } else if ((f != null) && f.equalsIgnoreCase("#end")) { return i; } } catch (BadLocationException e) { } return 0; } private int getStringEnd(IDocument d, int pos, int end, char ch) throws BadLocationException { while (pos < end) { char curr = d.getChar(pos); pos++; if (curr == '\\') { pos++; } else if (curr == ch) { return pos; } } return end; } private String smartindentonVeloTab(IDocument doc, int cmdoffset) { try { int len = doc.getLength(); int p = (cmdoffset != len) ? cmdoffset : (cmdoffset - 1); int line = doc.getLineOfOffset(p); int start = doc.getLineOffset(line); int whiteend = findEndOfWhiteSpace(doc, start, start + doc.getLineLength(line)); int indent; if ((isEndTag(whiteend, doc)) > 0) { int matchoffset = VelocityAutoIndentStrategy.findStartVeloBefore(start, doc); if (matchoffset < 0) { return ""; } indent = getIndentOfLine(doc, doc.getLineOfOffset(matchoffset)); } else { indent = 0; line = getNonBlankLineAbove(line, doc); if (line >= 0) { indent = getIndentOfLine(doc, line); indent = unIndentOnTagEnd(indent, whiteend, doc); int n = getTagCount(doc, doc.getLineOffset(line), start, true); if (n > 0) { indent = indent(indent, n); } else if (n < 0) { indent = unIndent(indent, -n); } } } if (indent == indentWidthOf(doc.get(start, whiteend - start), 4)) { indent = indent(indent, 1); } return getIndentString(indent).toString(); } catch (BadLocationException e) { } return ""; } private String smartIndentOnTab(IDocument doc, int originalCmdOffset) { if ((originalCmdOffset == -1) || (doc.getLength() == 0)) { return ""; } try { int len = doc.getLength(); int p = (originalCmdOffset != len) ? originalCmdOffset : (originalCmdOffset - 1); int line = doc.getLineOfOffset(p); int start = doc.getLineOffset(line); int whiteend = findEndOfWhiteSpace(doc, start, start + doc.getLineLength(line)); String f = VelocityAutoIndentStrategy.getVeloIdentifier(doc, whiteend, whiteend + 5); if ((f != null) && f.equals("#end")) { return smartindentonVeloTab(doc, originalCmdOffset); } if (originalCmdOffset > whiteend) { return ""; } int indent; int i = 0; if ((i = isEndTag(whiteend, doc)) > 0) { int matchoffset = VelocityAutoIndentStrategy.findMatchingOpenTagBefore(i, doc); if (matchoffset < 0) { return ""; } indent = getIndentOfLine(doc, doc.getLineOfOffset(matchoffset)); } else { indent = 0; line = getNonBlankLineAbove(line, doc); if (line >= 0) { indent = getIndentOfLine(doc, line); indent = unIndentOnTagEnd(indent, whiteend, doc); int n = getTagCount(doc, doc.getLineOffset(line), start, true); if (n > 0) { indent = indent(indent, n); } else if (n < 0) { indent = unIndent(indent, -n); } } } if (indent == indentWidthOf(doc.get(start, whiteend - start), 4)) { indent = indent(indent, 1); } return getIndentString(indent).toString(); } catch (BadLocationException e) { } return ""; } public static int indentWidthOf(String str, int tabwidth) { return VelocityAutoIndentStrategy.indentWidthOf(str, 0, tabwidth); } public StringBuffer getIndentString(int indent) { StringBuffer ret = new StringBuffer(); int n = indent / 4; for (int i = 0; i < n; i++) { ret.append(" "); } n = indent - (n * 4); for (int i = 0; i < n; i++) { ret.append(' '); } return ret; } public int indent(int indent, int n) { return ((indent / 4) + n) * 4; } public int unIndent(int indent, int n) { return ((((indent - 1) / 4) - n) + 1) * 4; } public int getTagCount(IDocument document, int start, int end, boolean skip) throws BadLocationException { int bracketcount = 0; while (start < end) { if (EditorsUtil.isInsidePartition(start, VelocityConfiguration.ESCAPED_PARTITIONS, true, document)) { org.eclipse.jface.text.ITypedRegion partition = document.getPartition(start); start = partition.getOffset() + partition.getLength(); } char c = document.getChar(start); start++; switch (c) { default: break; case 35: // '#' { if (start >= end) { break; } char c1 = document.getChar(start); if (c1 != '#') { String id = VelocityAutoIndentStrategy.getIdentifier(document, start, end + 1).toLowerCase(); if (Arrays.asList(Directive.INDENT_DIRECTIVES).contains(id)) { bracketcount++; skip = false; } break; } if (!skip) { bracketcount--; start++; } break; } case 37: // '%' case 47: // '/' case 63: // '?' { if (start >= end) { break; } char c1 = document.getChar(start); if ((c1 == '>') && !skip) { bracketcount--; start++; } break; } case 60: // '<' { if (start >= end) { break; } char c1 = document.getChar(start); if ((c1 == '!') || (c1 == '?') || (c1 == '%') || (c1 == '#')) { skip = false; break; } if (c1 != '/') { String id = VelocityAutoIndentStrategy.getIdentifier(document, start, end).toUpperCase(); // if // (!VelocityAutoIndentStrategy.fEMPTY_TAG_SET.contains(id)) // { bracketcount++; skip = false; // } break; } if (!skip) { bracketcount--; start++; } break; } case 34: // '"' case 39: // '\'' { start = getStringEnd(document, start, end, c); break; } } } return bracketcount; } /* * (non-Javadoc) * * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) */ public void run(IAction action) { if (_selected == null) { MessageDialog.openInformation(new Shell(), "VelocityPlugin", "Unable to open file"); // VelocityPlugin.log("Unable to open file"); return; } if (_selected instanceof IStructuredSelection) { // try { Object[] items = ((IStructuredSelection) _selected).toArray(); Set files = new HashSet(items.length, 1.0F); try { for (int i = 0; i < items.length; i++) { if (items[i] instanceof IResource) { IResource resource = (IResource) items[i]; switch (resource.getType()) { case IResource.FOLDER: case IResource.PROJECT: IContainer folder = (IContainer) items[i]; getChildren(folder, files); break; case IResource.FILE: files.add((IFile) items[i]); // ((IFile) items[i]).getProject() break; default: /** * @todo use logger to print warning about * invalid type */ break; } } } } catch (CoreException ex) { ex.printStackTrace(); } for (Iterator iter = files.iterator(); iter.hasNext();) { IFile directory = (IFile) iter.next(); // sortFile(directory); // run(); } } catch (Exception e) { VelocityPlugin.log(e); } } else { MessageDialog.openInformation(new Shell(), "VelocityPlugin", "Unable to open file"); // VelocityPlugin.log("Unable to open shell"); return; } } /* * (non-Javadoc) * * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, * org.eclipse.jface.viewers.ISelection) */ public void selectionChanged(IAction action, ISelection selection) { _selected = null; if (selection instanceof IStructuredSelection) { _selected = (IStructuredSelection) selection; } } private void getChildren(IContainer resource, Set files) throws CoreException { IResource[] children = resource.members(); for (int i = 0; i < children.length; i++) { IResource child = children[i]; switch (child.getType()) { case IResource.FILE: if (child.getName().endsWith(".vm") || child.getName().endsWith(".html")) { files.add((IFile) child); } break; case IResource.FOLDER: getChildren((IFolder) child, files); break; case IResource.PROJECT: getChildren((IProject) child, files); break; case IResource.ROOT: getChildren((IWorkspaceRoot) child, files); break; } } } private void formatFile(IFile file) { if (file.getFileExtension().equalsIgnoreCase("vm") || file.getFileExtension().equalsIgnoreCase("html")) { ISelection s = new StructuredSelection(file); // provider.setSelection(s); String msg = "Are you sure you want to format \'" + file.getName() + "\'?"; //$NON-NLS-1$ if (!MessageDialog.openQuestion(new Shell(), "Confirm formatting", msg)) { return; } // Properties p = new Properties(); IDocument document = new Document(); BufferedWriter awriter = null; String line = null; StringBuffer b = new StringBuffer(); try { BufferedReader in = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(file.getLocation().toFile())))); while ((line = in.readLine()) != null) { b.append(line); b.append(LINE_SEP); } document.set(b.toString()); document = format(document); awriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file.getLocation().toFile()), "8859_1")); awriter.write(document.get()); awriter.flush(); awriter.close(); if (file instanceof IResource) { ((IResource) file).refreshLocal(IResource.DEPTH_ZERO, null); } } catch (Exception e) { VelocityPlugin.log(e); } finally { if (awriter != null) { try { awriter.close(); } catch (IOException e) { VelocityPlugin.log(e); } } } } } }