/******************************************************************************* * Copyright (c) 2010 xored software, Inc. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * xored software, Inc. - initial API and Implementation (Alex Panchenko) *******************************************************************************/ package org.eclipse.dltk.javascript.internal.corext.codemanipulation; import static org.eclipse.dltk.javascript.ast.MultiLineComment.JSDOC_PREFIX; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import org.eclipse.dltk.core.IMethod; import org.eclipse.dltk.core.IParameter; import org.eclipse.dltk.core.IPreferencesLookupDelegate; import org.eclipse.dltk.core.IScriptProject; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.core.ISourceRange; import org.eclipse.dltk.core.ModelException; import org.eclipse.dltk.core.PreferencesLookupDelegate; import org.eclipse.dltk.javascript.internal.ui.JavaScriptUI; import org.eclipse.dltk.javascript.scriptdoc.IndentManipulation; import org.eclipse.dltk.javascript.scriptdoc.ScriptdocContentAccess; import org.eclipse.dltk.javascript.typeinfo.ITypeNames; import org.eclipse.dltk.ui.CodeFormatterConstants; public class JSCodeGeneration { public static String getMethodComment(IMethod meth, IMethod overridden, String lineDelimiter) { String existingComment = null; try { ISourceRange range = ScriptdocContentAccess.getJavadocRange(meth); if (range != null) { ISourceModule compilationUnit = meth.getSourceModule(); existingComment = compilationUnit.getBuffer().getText( range.getOffset(), range.getLength()); } } catch (Exception e) { // ignore } StringBuffer buf = new StringBuffer(); if (existingComment != null && !isEmptyComment(existingComment)) { // update existing doc. List<String> lines = new ArrayList<String>(); List<Param> paramLines = new ArrayList<Param>(); int paramStart = -1; boolean hasReturn = false; StringTokenizer st = new StringTokenizer(existingComment, lineDelimiter); while (st.hasMoreTokens()) { String line = st.nextToken().trim(); if (line.startsWith("*")) line = ' ' + line; hasReturn = hasReturn || line.indexOf("@return") != -1; int index = line.indexOf("@param"); if (index != -1) { if (paramStart == -1) paramStart = lines.size() - 1; Param param = new Param(); StringTokenizer tokenizer = new StringTokenizer(line, " \t"); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (token.equals("@param")) { if (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken(); if (token.startsWith("{") && token.endsWith("}")) { param.type = token; if (tokenizer.hasMoreTokens()) { param.name = tokenizer.nextToken(); } } else { param.name = token; } if (param.name.startsWith("[") && param.name.endsWith("]")) { param.optional = true; param.name = param.name.substring(1, param.name.length() - 1); } } if (tokenizer.hasMoreTokens()) { param.doc = tokenizer.nextToken(lineDelimiter) .trim(); } break; } } paramLines.add(param); } else lines.add(line); } int parameterLength = 0; try { IParameter[] parameters = meth.getParameters(); parameterLength = parameters.length; outer: for (int paramCounter = 0; paramCounter < parameterLength; paramCounter++) { IParameter parameter = parameters[paramCounter]; for (int i = 0; i < paramLines.size(); i++) { Param param = paramLines.get(i); if (param.name.equals(parameter.getName())) { if (i != paramCounter) { paramLines.remove(i); paramLines.add(paramCounter, param); } continue outer; } } paramLines.add(new Param(parameter.getName(), "{Object}")); paramCounter--; } } catch (ModelException e) { // ignore } if (paramStart == -1) paramStart = lines.size() - 2; for (int i = 0; i < lines.size(); i++) { buf.append(lines.get(i)).append(lineDelimiter); if (paramStart == i) { boolean optional = false; for (int j = 0; j < parameterLength; j++) { Param param = paramLines.get(j); if (!optional) optional = param.optional; param.optional = optional; buf.append(param).append(lineDelimiter); } if (!hasReturn) { try { if (meth.getType() != null) { buf.append(" * @return {"); buf.append(meth.getType()); buf.append("}").append(lineDelimiter); //$NON-NLS-1$ } } catch (ModelException e) { e.printStackTrace(); } } } } } else { buf.append(JSDOC_PREFIX).append(lineDelimiter); try { for (IParameter parameter : meth.getParameters()) { buf.append(" * @param {Object} " + parameter.getName()) .append(lineDelimiter); } if (meth.getType() != null) { buf.append(" * @return {"); buf.append(meth.getType()); buf.append("}").append(lineDelimiter); //$NON-NLS-1$ } } catch (ModelException e) { // ignore } // try { // if (meth.getType() != null) { // buf.append(" * @return {"); // buf.append(meth.getType()); // buf.append("}").append(lineDelimiter); //$NON-NLS-1$ // } // } catch (ModelException e) { // // ignore // } buf.append(" */").append(lineDelimiter); //$NON-NLS-1$ } return buf.toString(); } public static String getFieldComment(ISourceModule sourceModule, String typeName, String fieldName, String lineDelimiter) { StringBuffer buf = new StringBuffer(); buf.append(JSDOC_PREFIX).append(lineDelimiter); buf.append(" * " + fieldName).append(lineDelimiter); //$NON-NLS-1$ // TODO (alex) do we need an option for braces here ? buf.append(" * @type ").append("{").append(typeName != null ? typeName : ITypeNames.OBJECT).append("}").append(lineDelimiter); //$NON-NLS-1$ buf.append(" */").append(lineDelimiter); //$NON-NLS-1$ return buf.toString(); } public static String getTypeComment(ISourceModule sourceModule, String typeQualifiedName, String[] typeParameterNames, String lineDelimiter) { // TODO Auto-generated method stub return null; } /** * Returns that part of the indentation of <code>line</code> that makes up a * multiple of indentation units. * * @param line * the line to scan * @param project * the java project from which to get the formatter preferences, * or <code>null</code> for global preferences * @return the indent part of <code>line</code>, but no odd spaces * @since 3.1 */ public static String getIndentString(String line, IScriptProject project) { return IndentManipulation.extractIndentString(line, getTabWidth(project), getIndentWidth(project)); } /** * Gets the current tab width. * * @param project * The project where the source is used, used for project * specific options or <code>null</code> if the project is * unknown and the workspace default should be used * @return The tab width */ public static int getTabWidth(IScriptProject project) { /* * If the tab-char is SPACE, FORMATTER_INDENTATION_SIZE is not used by * the core formatter. We piggy back the visual tab length setting in * that preference in that case. */ final String key; final IPreferencesLookupDelegate prefs = new PreferencesLookupDelegate( project); if (CodeFormatterConstants.SPACE.equals(prefs.getString( JavaScriptUI.PLUGIN_ID, CodeFormatterConstants.FORMATTER_TAB_CHAR))) { key = CodeFormatterConstants.FORMATTER_INDENTATION_SIZE; } else { key = CodeFormatterConstants.FORMATTER_TAB_SIZE; } return prefs.getInt(JavaScriptUI.PLUGIN_ID, key); } /** * Returns the current indent width. * * @param project * the project where the source is used or, <code>null</code> if * the project is unknown and the workspace default should be * used * @return the indent width */ public static int getIndentWidth(IScriptProject project) { String key; final IPreferencesLookupDelegate prefs = new PreferencesLookupDelegate( project); if (CodeFormatterConstants.MIXED.equals(prefs.getString( JavaScriptUI.PLUGIN_ID, CodeFormatterConstants.FORMATTER_TAB_CHAR))) key = CodeFormatterConstants.FORMATTER_INDENTATION_SIZE; else key = CodeFormatterConstants.FORMATTER_TAB_SIZE; return prefs.getInt(JavaScriptUI.PLUGIN_ID, key); } /** * Change the indent of, possible multi-line, code range. The current indent * is removed, a new indent added. The first line of the code will not be * changed. (It is considered to have no indent as it might start in the * middle of a line) * * @param code * the code * @param codeIndentLevel * level of indentation * * @param project * the java project from which to get the formatter preferences, * or <code>null</code> for global preferences * @param newIndent * new indent * @param lineDelim * line delimiter * @return the changed code */ public static String changeIndent(String code, int codeIndentLevel, IScriptProject project, String newIndent, String lineDelim) { return IndentManipulation.changeIndent(code, codeIndentLevel, getTabWidth(project), getIndentWidth(project), newIndent, lineDelim); } private static boolean isEmptyComment(String comment) { int begin = 0; int end = comment.length(); while (begin < end && Character.isWhitespace(comment.charAt(begin))) { ++begin; } if (begin < end && comment.charAt(begin) == '/') { ++begin; } while (begin < end && Character.isWhitespace(comment.charAt(end - 1))) { --end; } if (begin < end && comment.charAt(end - 1) == '/') { --end; } while (begin < end && (Character.isWhitespace(comment.charAt(begin)) || comment .charAt(begin) == '*')) { ++begin; } return begin >= end; } private static class Param { private String name; private boolean optional; private String type; private String doc; public Param() { } public Param(String name, String type) { this.name = name; this.type = type; } public String toString() { return " * @param " + (type != null ? type + " " : "") + (optional ? "[" + name + "]" : name) + (doc != null ? " " + doc : ""); } } }