| /******************************************************************************* |
| * Copyright (c) 2001, 2016 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Rational Software - initial implementation |
| * Anton Leherbauer (Wind River Systems) |
| * Jens Elmenthaler (Verigy) - http://bugs.eclipse.org/235586 |
| * Sergey Prigogin (Google) |
| *******************************************************************************/ |
| package org.eclipse.cdt.internal.corext.codemanipulation; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.UUID; |
| |
| import org.eclipse.cdt.core.CConventions; |
| import org.eclipse.cdt.core.CCorePlugin; |
| import org.eclipse.cdt.core.CCorePreferenceConstants; |
| import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage; |
| import org.eclipse.cdt.core.dom.parser.AbstractCLikeLanguage; |
| import org.eclipse.cdt.core.model.CModelException; |
| import org.eclipse.cdt.core.model.CoreModel; |
| import org.eclipse.cdt.core.model.IBuffer; |
| import org.eclipse.cdt.core.model.ICElement; |
| import org.eclipse.cdt.core.model.ICProject; |
| import org.eclipse.cdt.core.model.ILanguage; |
| import org.eclipse.cdt.core.model.ISourceRoot; |
| import org.eclipse.cdt.core.model.ITranslationUnit; |
| import org.eclipse.cdt.internal.corext.template.c.CodeTemplateContext; |
| import org.eclipse.cdt.internal.corext.template.c.CodeTemplateContextType; |
| import org.eclipse.cdt.internal.corext.template.c.FileTemplateContext; |
| import org.eclipse.cdt.internal.corext.template.c.FileTemplateContextType; |
| import org.eclipse.cdt.internal.corext.util.Strings; |
| import org.eclipse.cdt.internal.ui.text.CBreakIterator; |
| import org.eclipse.cdt.internal.ui.util.NameComposer; |
| import org.eclipse.cdt.internal.ui.viewsupport.ProjectTemplateStore; |
| import org.eclipse.cdt.ui.CUIPlugin; |
| import org.eclipse.cdt.ui.PreferenceConstants; |
| import org.eclipse.cdt.utils.PathUtil; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.ProjectScope; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.preferences.IPreferencesService; |
| import org.eclipse.core.runtime.preferences.IScopeContext; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.templates.Template; |
| import org.eclipse.jface.text.templates.TemplateBuffer; |
| import org.eclipse.jface.text.templates.TemplateContext; |
| import org.eclipse.jface.text.templates.TemplateException; |
| import org.eclipse.jface.text.templates.TemplateVariable; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.text.edits.DeleteEdit; |
| import org.eclipse.text.edits.InsertEdit; |
| import org.eclipse.text.edits.MalformedTreeException; |
| import org.eclipse.text.edits.MultiTextEdit; |
| import org.eclipse.text.templates.TemplatePersistenceData; |
| |
| import com.ibm.icu.text.BreakIterator; |
| |
| public class StubUtility { |
| private static final String[] EMPTY = {}; |
| |
| private StubUtility() { |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| * @see org.eclipse.cdt.ui.CodeGeneration#getHeaderFileContent(ITranslationUnit, String, String, String) |
| */ |
| public static String getHeaderFileContent(ITranslationUnit tu, String declarations, String fileComment, |
| String includes, String namespaceBegin, String namespaceEnd, String namespaceName, String typeComment, |
| String typeName, String lineDelimiter) throws CoreException { |
| return getHeaderFileContent(getDefaultFileTemplate(tu), tu, declarations, fileComment, includes, namespaceBegin, |
| namespaceEnd, namespaceName, typeComment, typeName, lineDelimiter); |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| * @see org.eclipse.cdt.ui.CodeGeneration#getHeaderFileContent(Template, ITranslationUnit, String, String, String) |
| */ |
| public static String getHeaderFileContent(Template template, ITranslationUnit tu, String declarations, |
| String fileComment, String includes, String namespaceBegin, String namespaceEnd, String namespaceName, |
| String typeComment, String typeName, String lineDelimiter) throws CoreException { |
| if (template == null) { |
| return null; |
| } |
| ICProject project = tu.getCProject(); |
| CodeTemplateContext context = new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter); |
| context.setTranslationUnitVariables(tu); |
| String includeGuardSymbol = generateIncludeGuardSymbol(tu.getResource(), project); |
| context.setVariable(CodeTemplateContextType.DECLARATIONS, declarations != null ? declarations : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.FILE_COMMENT, fileComment != null ? fileComment : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.INCLUDE_GUARD_SYMBOL, |
| includeGuardSymbol != null ? includeGuardSymbol : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.INCLUDES, includes != null ? includes : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.NAMESPACE_BEGIN, namespaceBegin != null ? namespaceBegin : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.NAMESPACE_END, namespaceEnd != null ? namespaceEnd : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.NAMESPACE_NAME, namespaceName != null ? namespaceName : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.TYPE_COMMENT, typeComment != null ? typeComment : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.TYPENAME, typeName != null ? typeName : ""); //$NON-NLS-1$ |
| String[] fullLine = { CodeTemplateContextType.DECLARATIONS, CodeTemplateContextType.FILE_COMMENT, |
| CodeTemplateContextType.INCLUDES, CodeTemplateContextType.NAMESPACE_BEGIN, |
| CodeTemplateContextType.NAMESPACE_END, CodeTemplateContextType.TYPE_COMMENT }; |
| |
| String text = evaluateTemplate(context, template, fullLine); |
| if (text != null && !text.endsWith(lineDelimiter)) |
| text += lineDelimiter; |
| return text; |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| * @see org.eclipse.cdt.ui.CodeGeneration#getBodyFileContent(ITranslationUnit, String, String, String, String, String, String, String, String, String) |
| */ |
| public static String getBodyFileContent(ITranslationUnit tu, String declarations, String fileComment, |
| String includes, String namespaceBegin, String namespaceEnd, String namespaceName, String typeComment, |
| String typeName, String lineDelimiter) throws CoreException { |
| return getBodyFileContent(getDefaultFileTemplate(tu), tu, declarations, fileComment, includes, namespaceBegin, |
| namespaceEnd, namespaceName, typeComment, typeName, lineDelimiter); |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| * @see org.eclipse.cdt.ui.CodeGeneration#getBodyFileContent(Template, ITranslationUnit, String, String, String, String, String, String, String, String, String) |
| */ |
| public static String getBodyFileContent(Template template, ITranslationUnit tu, String declarations, |
| String fileComment, String includes, String namespaceBegin, String namespaceEnd, String namespaceName, |
| String typeComment, String typeName, String lineDelimiter) throws CoreException { |
| if (template == null) { |
| return null; |
| } |
| ICProject project = tu.getCProject(); |
| CodeTemplateContext context = new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter); |
| context.setTranslationUnitVariables(tu); |
| context.setVariable(CodeTemplateContextType.DECLARATIONS, declarations != null ? declarations : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.FILE_COMMENT, fileComment != null ? fileComment : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.INCLUDES, includes != null ? includes : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.NAMESPACE_BEGIN, namespaceBegin != null ? namespaceBegin : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.NAMESPACE_END, namespaceEnd != null ? namespaceEnd : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.NAMESPACE_NAME, namespaceName != null ? namespaceName : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.TYPE_COMMENT, typeComment != null ? typeComment : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.TYPENAME, typeName != null ? typeName : ""); //$NON-NLS-1$ |
| String[] fullLine = { CodeTemplateContextType.DECLARATIONS, CodeTemplateContextType.FILE_COMMENT, |
| CodeTemplateContextType.INCLUDES, CodeTemplateContextType.NAMESPACE_BEGIN, |
| CodeTemplateContextType.NAMESPACE_END, CodeTemplateContextType.TYPE_COMMENT }; |
| String text = evaluateTemplate(context, template, fullLine); |
| if (text != null && !text.endsWith(lineDelimiter)) |
| text += lineDelimiter; |
| return text; |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| * @see org.eclipse.cdt.ui.CodeGeneration#getTestFileContent(ITranslationUnit, String, String, String) |
| */ |
| public static String getTestFileContent(ITranslationUnit tu, String declarations, String fileComment, |
| String includes, String namespaceBegin, String namespaceEnd, String namespaceName, String typeName, |
| String lineDelimiter) throws CoreException { |
| return getBodyFileContent(getTestFileTemplate(tu), tu, declarations, fileComment, includes, namespaceBegin, |
| namespaceEnd, namespaceName, null, typeName, lineDelimiter); |
| } |
| |
| public static String getFileContent(Template template, IFile file, String lineDelimiter) throws CoreException { |
| ICProject cproject = null; |
| final IProject project = file.getProject(); |
| if (CoreModel.hasCNature(project)) { |
| cproject = CoreModel.getDefault().create(project); |
| } |
| FileTemplateContext context; |
| if (cproject != null) { |
| context = new CodeTemplateContext(template.getContextTypeId(), cproject, lineDelimiter); |
| } else { |
| context = new FileTemplateContext(template.getContextTypeId(), lineDelimiter); |
| } |
| String fileComment = getFileComment(file, lineDelimiter); |
| context.setVariable(CodeTemplateContextType.FILE_COMMENT, fileComment != null ? fileComment : ""); //$NON-NLS-1$ |
| String includeGuardSymbol = generateIncludeGuardSymbol(file, cproject); |
| context.setVariable(CodeTemplateContextType.INCLUDE_GUARD_SYMBOL, |
| includeGuardSymbol != null ? includeGuardSymbol : ""); //$NON-NLS-1$ |
| context.setResourceVariables(file); |
| String[] fullLine = { CodeTemplateContextType.FILE_COMMENT }; |
| |
| String text = evaluateTemplate(context, template, fullLine); |
| if (text != null) { |
| // Remove blank lines at the end. |
| int len = text.length(); |
| while (true) { |
| int offset = len - lineDelimiter.length(); |
| if (!text.startsWith(lineDelimiter, offset)) |
| break; |
| len = offset; |
| } |
| len += lineDelimiter.length(); |
| if (len < text.length()) { |
| text = text.substring(0, len); |
| } else if (!text.endsWith(lineDelimiter)) { |
| text += lineDelimiter; // Add a line delimiter at the end. |
| } |
| } |
| return text; |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| */ |
| public static String getClassBodyContent(ICProject project, String className, String classMemberDeclarations, |
| String lineDelimiter) throws CoreException { |
| Template template = getCodeTemplate(CodeTemplateContextType.CLASS_BODY_ID, project); |
| if (template == null) { |
| return classMemberDeclarations; |
| } |
| CodeTemplateContext context = new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter); |
| context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, className); |
| context.setVariable(CodeTemplateContextType.DECLARATIONS, |
| classMemberDeclarations != null ? classMemberDeclarations : ""); //$NON-NLS-1$ |
| String str = evaluateTemplate(context, template, new String[] { CodeTemplateContextType.DECLARATIONS }); |
| if (str == null && classMemberDeclarations != null |
| && !Strings.containsOnlyWhitespaces(classMemberDeclarations)) { |
| return classMemberDeclarations; |
| } |
| return str; |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| */ |
| public static String getMethodBodyContent(ICProject project, String typeName, String methodName, |
| String bodyStatement, String lineDelimiter) throws CoreException { |
| String templateId = CodeTemplateContextType.METHODSTUB_ID; |
| return getMethodBodyContent(templateId, project, typeName, methodName, bodyStatement, lineDelimiter); |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| */ |
| public static String getMethodBodyContent(String templateId, ICProject project, String typeName, String methodName, |
| String bodyStatement, String lineDelimiter) throws CoreException { |
| Template template = getCodeTemplate(templateId, project); |
| if (template == null) { |
| return bodyStatement; |
| } |
| CodeTemplateContext context = new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter); |
| context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName); |
| context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, typeName); |
| context.setVariable(CodeTemplateContextType.BODY_STATEMENT, bodyStatement != null ? bodyStatement : ""); //$NON-NLS-1$ |
| String str = evaluateTemplate(context, template, new String[] { CodeTemplateContextType.BODY_STATEMENT }); |
| if (str == null && bodyStatement != null && !Strings.containsOnlyWhitespaces(bodyStatement)) { |
| return bodyStatement; |
| } |
| return str; |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| */ |
| public static String getConstructorBodyContent(ICProject project, String typeName, String bodyStatement, |
| String lineDelimiter) throws CoreException { |
| String templateId = CodeTemplateContextType.CONSTRUCTORSTUB_ID; |
| return getMethodBodyContent(templateId, project, typeName, typeName, bodyStatement, lineDelimiter); |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| */ |
| public static String getDestructorBodyContent(ICProject project, String typeName, String bodyStatement, |
| String lineDelimiter) throws CoreException { |
| String templateId = CodeTemplateContextType.DESTRUCTORSTUB_ID; |
| return getMethodBodyContent(templateId, project, typeName, "~" + typeName, bodyStatement, lineDelimiter); //$NON-NLS-1$ |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| */ |
| public static String getNamespaceBeginContent(ICProject project, String namespaceName, String lineDelimiter) |
| throws CoreException { |
| Template template = getCodeTemplate(CodeTemplateContextType.NAMESPACE_BEGIN_ID, project); |
| if (template == null) { |
| return null; |
| } |
| CodeTemplateContext context = new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter); |
| context.setVariable(CodeTemplateContextType.NAMESPACE_NAME, namespaceName); |
| return evaluateTemplate(context, template, EMPTY); |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| */ |
| public static String getNamespaceEndContent(ICProject project, String namespaceName, String lineDelimiter) |
| throws CoreException { |
| Template template = getCodeTemplate(CodeTemplateContextType.NAMESPACE_END_ID, project); |
| if (template == null) { |
| return null; |
| } |
| CodeTemplateContext context = new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter); |
| context.setVariable(CodeTemplateContextType.NAMESPACE_NAME, namespaceName); |
| return evaluateTemplate(context, template, EMPTY); |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| * @see org.eclipse.cdt.ui.CodeGeneration#getFileComment(ITranslationUnit, String) |
| */ |
| public static String getFileComment(ITranslationUnit tu, String lineDelimiter) throws CoreException { |
| Template template = getCodeTemplate(CodeTemplateContextType.FILECOMMENT_ID, tu.getCProject()); |
| if (template == null) { |
| return null; |
| } |
| |
| ICProject project = tu.getCProject(); |
| CodeTemplateContext context = new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter); |
| context.setTranslationUnitVariables(tu); |
| return evaluateTemplate(context, template); |
| } |
| |
| private static String getFileComment(IFile file, String lineDelimiter) throws CoreException { |
| Template template = getCodeTemplate(CodeTemplateContextType.FILECOMMENT_ID, file.getProject()); |
| if (template == null) { |
| return null; |
| } |
| |
| FileTemplateContext context = new FileTemplateContext(template.getContextTypeId(), lineDelimiter); |
| context.setResourceVariables(file); |
| return evaluateTemplate(context, template); |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| * @see org.eclipse.cdt.ui.CodeGeneration#getClassComment(ITranslationUnit, String, String) |
| */ |
| public static String getClassComment(ITranslationUnit tu, String typeQualifiedName, String lineDelimiter) |
| throws CoreException { |
| Template template = getCodeTemplate(CodeTemplateContextType.TYPECOMMENT_ID, tu.getCProject()); |
| if (template == null) { |
| return null; |
| } |
| |
| ICProject project = tu.getCProject(); |
| CodeTemplateContext context = new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter); |
| context.setTranslationUnitVariables(tu); |
| context.setVariable(CodeTemplateContextType.TYPENAME, typeQualifiedName); |
| return evaluateTemplate(context, template); |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| * @see org.eclipse.cdt.ui.CodeGeneration#getMethodComment(ITranslationUnit, String, String, String[], String[], String, String) |
| */ |
| public static String getMethodComment(ITranslationUnit tu, String typeName, String methodName, String[] paramNames, |
| String[] excTypeSig, String retTypeSig, String lineDelimiter) throws CoreException { |
| String templateId = CodeTemplateContextType.METHODCOMMENT_ID; |
| return getMethodComment(templateId, tu, typeName, methodName, paramNames, excTypeSig, retTypeSig, |
| lineDelimiter); |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| * @see org.eclipse.cdt.ui.CodeGeneration#getConstructorComment(ITranslationUnit, String, String[], String[], String) |
| */ |
| public static String getConstructorComment(ITranslationUnit tu, String typeName, String[] paramNames, |
| String[] excTypeSig, String lineDelimiter) throws CoreException { |
| String templateId = CodeTemplateContextType.CONSTRUCTORCOMMENT_ID; |
| return getMethodComment(templateId, tu, typeName, typeName, paramNames, excTypeSig, null, lineDelimiter); |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| * @see org.eclipse.cdt.ui.CodeGeneration#getDestructorComment(ITranslationUnit, String, String[], String) |
| */ |
| public static String getDestructorComment(ITranslationUnit tu, String typeName, String[] excTypeSig, |
| String lineDelimiter) throws CoreException { |
| String templateId = CodeTemplateContextType.DESTRUCTORCOMMENT_ID; |
| return getMethodComment(templateId, tu, typeName, "~" + typeName, EMPTY, excTypeSig, null, lineDelimiter); //$NON-NLS-1$ |
| } |
| |
| /* |
| * Don't use this method directly, use CodeGeneration. |
| * @see org.eclipse.cdt.ui.CodeGeneration#getMethodComment(ITranslationUnit, String, String, String[], String[], String, String) |
| */ |
| public static String getMethodComment(String templateId, ITranslationUnit tu, String typeName, String methodName, |
| String[] paramNames, String[] excTypeSig, String retTypeSig, String lineDelimiter) throws CoreException { |
| Template template = getCodeTemplate(templateId, tu.getCProject()); |
| if (template == null) { |
| return null; |
| } |
| CodeTemplateContext context = new CodeTemplateContext(template.getContextTypeId(), tu.getCProject(), |
| lineDelimiter); |
| context.setTranslationUnitVariables(tu); |
| context.setVariable(CodeTemplateContextType.ENCLOSING_TYPE, typeName); |
| context.setVariable(CodeTemplateContextType.ENCLOSING_METHOD, methodName); |
| |
| if (retTypeSig != null) { |
| context.setVariable(CodeTemplateContextType.RETURN_TYPE, retTypeSig); |
| } |
| context.setTranslationUnitVariables(tu); |
| TemplateBuffer buffer; |
| try { |
| buffer = context.evaluate(template); |
| } catch (BadLocationException e) { |
| throw new CoreException(Status.CANCEL_STATUS); |
| } catch (TemplateException e) { |
| throw new CoreException(Status.CANCEL_STATUS); |
| } |
| if (buffer == null) { |
| return null; |
| } |
| |
| // TODO doc comment tags |
| |
| String str = buffer.getString(); |
| if (Strings.containsOnlyWhitespaces(str)) { |
| return null; |
| } |
| |
| return str; |
| } |
| |
| // remove lines for empty variables, prefix multi-line variables |
| private static String fixFullLineVariables(TemplateBuffer buffer, String[] variables) |
| throws MalformedTreeException, BadLocationException { |
| IDocument doc = new Document(buffer.getString()); |
| int nLines = doc.getNumberOfLines(); |
| MultiTextEdit edit = new MultiTextEdit(); |
| HashSet<Integer> removedLines = new HashSet<>(); |
| for (int i = 0; i < variables.length; i++) { |
| TemplateVariable position = findVariable(buffer, variables[i]); |
| if (position == null) { |
| continue; |
| } |
| if (position.getLength() > 0) { |
| int[] offsets = position.getOffsets(); |
| for (int j = 0; j < offsets.length; j++) { |
| final int offset = offsets[j]; |
| try { |
| int startLine = doc.getLineOfOffset(offset); |
| int startOffset = doc.getLineOffset(startLine); |
| int endLine = doc.getLineOfOffset(offset + position.getLength()); |
| String prefix = doc.get(startOffset, offset - startOffset); |
| if (prefix.length() > 0 && startLine < endLine) { |
| for (int line = startLine + 1; line <= endLine; ++line) { |
| int lineOffset = doc.getLineOffset(line); |
| edit.addChild(new InsertEdit(lineOffset, prefix)); |
| } |
| } |
| } catch (BadLocationException e) { |
| break; |
| } |
| } |
| } else { |
| int[] offsets = position.getOffsets(); |
| for (int k = 0; k < offsets.length; k++) { |
| int line = doc.getLineOfOffset(offsets[k]); |
| IRegion lineInfo = doc.getLineInformation(line); |
| int offset = lineInfo.getOffset(); |
| String str = doc.get(offset, lineInfo.getLength()); |
| if (Strings.containsOnlyWhitespaces(str) && nLines > line + 1 |
| && removedLines.add(Integer.valueOf(line))) { |
| int nextStart = doc.getLineOffset(line + 1); |
| int length = nextStart - offset; |
| edit.addChild(new DeleteEdit(offset, length)); |
| } |
| } |
| } |
| } |
| edit.apply(doc, 0); |
| return doc.get(); |
| } |
| |
| private static TemplateVariable findVariable(TemplateBuffer buffer, String variable) { |
| TemplateVariable[] positions = buffer.getVariables(); |
| for (int i = 0; i < positions.length; i++) { |
| TemplateVariable curr = positions[i]; |
| if (variable.equals(curr.getType())) { |
| return curr; |
| } |
| } |
| return null; |
| } |
| |
| private static String evaluateTemplate(TemplateContext context, Template template) throws CoreException { |
| TemplateBuffer buffer; |
| try { |
| buffer = context.evaluate(template); |
| } catch (BadLocationException e) { |
| throw new CoreException(Status.CANCEL_STATUS); |
| } catch (TemplateException e) { |
| throw new CoreException(Status.CANCEL_STATUS); |
| } |
| if (buffer == null) |
| return null; |
| String str = buffer.getString(); |
| if (Strings.containsOnlyWhitespaces(str)) { |
| return null; |
| } |
| return str; |
| } |
| |
| private static String evaluateTemplate(TemplateContext context, Template template, String[] fullLineVariables) |
| throws CoreException { |
| TemplateBuffer buffer; |
| try { |
| buffer = context.evaluate(template); |
| if (buffer == null) |
| return null; |
| String str = fixFullLineVariables(buffer, fullLineVariables); |
| if (Strings.containsOnlyWhitespaces(str)) { |
| return null; |
| } |
| return str; |
| } catch (BadLocationException e) { |
| throw new CoreException(Status.CANCEL_STATUS); |
| } catch (TemplateException e) { |
| throw new CoreException(Status.CANCEL_STATUS); |
| } |
| } |
| |
| /** |
| * Returns the line delimiter which is used in the specified project. |
| * |
| * @param project the C project, or <code>null</code> |
| * @return the used line delimiter |
| */ |
| public static String getLineDelimiterUsed(ICProject project) { |
| return getProjectLineDelimiter(project); |
| } |
| |
| private static String getProjectLineDelimiter(ICProject cProject) { |
| IProject project = null; |
| if (cProject != null) |
| project = cProject.getProject(); |
| |
| String lineDelimiter = getLineDelimiterPreference(project); |
| if (lineDelimiter != null) |
| return lineDelimiter; |
| |
| return System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| public static String getLineDelimiterPreference(IProject project) { |
| IScopeContext[] scopeContext; |
| if (project != null) { |
| // project preference |
| scopeContext = new IScopeContext[] { new ProjectScope(project) }; |
| String lineDelimiter = Platform.getPreferencesService().getString(Platform.PI_RUNTIME, |
| Platform.PREF_LINE_SEPARATOR, null, scopeContext); |
| if (lineDelimiter != null) |
| return lineDelimiter; |
| } |
| // workspace preference |
| scopeContext = new IScopeContext[] { InstanceScope.INSTANCE }; |
| String platformDefault = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| return Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, |
| platformDefault, scopeContext); |
| } |
| |
| /** |
| * Examines a string and returns the first line delimiter found. |
| */ |
| public static String getLineDelimiterUsed(ICElement elem) throws CModelException { |
| if (elem == null) |
| return ""; //$NON-NLS-1$ |
| |
| ITranslationUnit cu = (ITranslationUnit) elem.getAncestor(ICElement.C_UNIT); |
| if (cu != null && cu.exists()) { |
| IBuffer buf = cu.getBuffer(); |
| int length = buf.getLength(); |
| for (int i = 0; i < length; i++) { |
| char ch = buf.getChar(i); |
| if (ch == SWT.CR) { |
| if (i + 1 < length) { |
| if (buf.getChar(i + 1) == SWT.LF) { |
| return "\r\n"; //$NON-NLS-1$ |
| } |
| } |
| return "\r"; //$NON-NLS-1$ |
| } else if (ch == SWT.LF) { |
| return "\n"; //$NON-NLS-1$ |
| } |
| } |
| } |
| return getProjectLineDelimiter(elem.getCProject()); |
| } |
| |
| /** |
| * Get the default task tag for the given project. |
| * |
| * @param project |
| * @return the default task tag |
| */ |
| public static String getTodoTaskTag(ICProject project) { |
| String markers = null; |
| if (project == null) { |
| markers = CCorePlugin.getOption(CCorePreferenceConstants.TODO_TASK_TAGS); |
| } else { |
| markers = project.getOption(CCorePreferenceConstants.TODO_TASK_TAGS, true); |
| } |
| |
| if (markers != null && markers.length() > 0) { |
| int idx = markers.indexOf(','); |
| if (idx == -1) { |
| return markers; |
| } |
| return markers.substring(0, idx); |
| } |
| return CCorePreferenceConstants.DEFAULT_TASK_TAG; |
| } |
| |
| public static boolean doAddComments(ICProject project) { |
| return PreferenceConstants.getPreference(PreferenceConstants.CODEGEN_ADD_COMMENTS, project, false); |
| } |
| |
| private static Template getDefaultFileTemplate(ITranslationUnit tu) { |
| String templateId = null; |
| if (tu.isASMLanguage()) { |
| templateId = CodeTemplateContextType.ASM_SOURCEFILE_ID; |
| } else if (tu.isCXXLanguage()) { |
| if (tu.isHeaderUnit()) { |
| templateId = CodeTemplateContextType.CPP_HEADERFILE_ID; |
| } else { |
| templateId = CodeTemplateContextType.CPP_SOURCEFILE_ID; |
| } |
| } else if (tu.isCLanguage()) { |
| if (tu.isHeaderUnit()) { |
| templateId = CodeTemplateContextType.C_HEADERFILE_ID; |
| } else { |
| templateId = CodeTemplateContextType.C_SOURCEFILE_ID; |
| } |
| } |
| return getCodeTemplate(templateId, tu.getCProject()); |
| } |
| |
| private static Template getTestFileTemplate(ITranslationUnit tu) { |
| String templateId = null; |
| if (tu.isCXXLanguage() && !tu.isHeaderUnit()) { |
| templateId = CodeTemplateContextType.CPP_TESTFILE_ID; |
| } |
| return getCodeTemplate(templateId, tu.getCProject()); |
| } |
| |
| private static Template getCodeTemplate(String id, ICProject cProject) { |
| return getCodeTemplate(id, cProject != null ? cProject.getProject() : null); |
| } |
| |
| private static Template getCodeTemplate(String id, IProject project) { |
| if (project == null) |
| return CUIPlugin.getDefault().getCodeTemplateStore().findTemplateById(id); |
| ProjectTemplateStore projectStore = new ProjectTemplateStore(project); |
| try { |
| projectStore.load(); |
| } catch (IOException e) { |
| CUIPlugin.log(e); |
| } |
| return projectStore.findTemplateById(id); |
| } |
| |
| public static String generateIncludeGuardSymbol(IResource file, ICProject cproject) { |
| int scheme = PreferenceConstants.getPreference(PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME, |
| cproject, PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME_FILE_NAME); |
| |
| switch (scheme) { |
| case PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME_FILE_PATH: |
| if (file == null) |
| return null; |
| IPath path = file.getFullPath(); |
| ISourceRoot root = cproject.findSourceRoot(file); |
| IPath basePath = root == null ? cproject.getPath() : root.getPath(); |
| path = PathUtil.makeRelativePath(path, basePath); |
| return generateIncludeGuardSymbolFromFilePath(path.toString()); |
| |
| default: |
| CUIPlugin.log("Unknown preference value " + scheme + " for include guard scheme.", null); //$NON-NLS-1$ //$NON-NLS-2$ |
| //$FALL-THROUGH$ |
| case PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME_FILE_NAME: |
| if (file == null) |
| return null; |
| return generateIncludeGuardSymbolFromFilePath(file.getName()); |
| |
| case PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME_UUID: |
| return generateIncludeGuardSymbolFromUUID(); |
| } |
| } |
| |
| public static String generateIncludeGuardSymbolFromFilePath(String filename) { |
| // Convert to upper case and replace invalid characters with underscores, |
| // e.g. convert some/directory/foo-bar.h to SOME_DIRECTORY_FOO_BAR_H_ |
| StringBuilder buf = new StringBuilder(filename.length() + 1); |
| for (int i = 0; i < filename.length(); ++i) { |
| char ch = filename.charAt(i); |
| if (Character.isLetterOrDigit(ch)) { |
| buf.append(Character.toUpperCase(ch)); |
| } else if (buf.length() > 0) { |
| buf.append('_'); |
| } |
| } |
| buf.append('_'); |
| return buf.toString(); |
| } |
| |
| private static String generateIncludeGuardSymbolFromUUID() { |
| String uuid = UUID.randomUUID().toString(); |
| |
| // 1) Make sure the guard always starts with a letter. |
| // 2) Convert to upper case and remove invalid characters |
| // |
| // e.g. convert |
| // 067e6162-3b6f-4ae2-a171-2470b63dff00 to |
| // H067E6162-3b6F-4AE2-A171-2470B63DFF00 |
| StringBuilder buf = new StringBuilder(); |
| |
| buf.append('H'); |
| |
| for (int i = 0; i < uuid.length(); ++i) { |
| char ch = uuid.charAt(i); |
| if (Character.isLetterOrDigit(ch)) { |
| buf.append(Character.toUpperCase(ch)); |
| } else { |
| buf.append('_'); |
| } |
| } |
| |
| return buf.toString(); |
| } |
| |
| /** |
| * Get a set of file templates for the given content types. |
| * |
| * @param contentTypes the list of content types |
| * @param project the project or <code>null</code> |
| * @return an array of templates |
| */ |
| public static Template[] getFileTemplatesForContentTypes(String[] contentTypes, IProject project) { |
| if (contentTypes == null || contentTypes.length == 0) { |
| return new Template[0]; |
| } |
| TemplatePersistenceData[] templateDatas; |
| if (project == null) { |
| templateDatas = CUIPlugin.getDefault().getCodeTemplateStore().getTemplateData(true); |
| } else { |
| ProjectTemplateStore projectStore = new ProjectTemplateStore(project.getProject()); |
| try { |
| projectStore.load(); |
| } catch (IOException e) { |
| CUIPlugin.log(e); |
| } |
| templateDatas = projectStore.getTemplateData(); |
| } |
| List<Template> result = new ArrayList<>(); |
| for (int j = 0; j < contentTypes.length; j++) { |
| for (int i = 0; i < templateDatas.length; i++) { |
| Template template = templateDatas[i].getTemplate(); |
| final String contextTypeId = template.getContextTypeId(); |
| if (FileTemplateContextType.isContextTypeForContentType(contextTypeId, contentTypes[j])) { |
| result.add(template); |
| } |
| } |
| } |
| return result.toArray(new Template[result.size()]); |
| } |
| |
| /** |
| * Returns a suggested name for a getter that is guaranteed to be a valid identifier |
| * and not collide with a set of given names. |
| * |
| * @param baseName the name used as an inspiration |
| * @param bool <code>true</code> if the getter is for a boolean field |
| * @param excluded the set of excluded names, can be {@code null} |
| * @param context the translation unit for which the code is intended, can be {@code null} |
| * @return the suggested name, or {@code null} if all possible names are taken |
| */ |
| public static String suggestGetterName(String baseName, boolean bool, Set<String> excluded, |
| ITranslationUnit context) { |
| IPreferencesService preferences = Platform.getPreferencesService(); |
| int capitalization = preferences.getInt(CUIPlugin.PLUGIN_ID, |
| PreferenceConstants.NAME_STYLE_GETTER_CAPITALIZATION, |
| PreferenceConstants.NAME_STYLE_CAPITALIZATION_CAMEL_CASE, null); |
| String wordDelimiter = preferences.getString(CUIPlugin.PLUGIN_ID, |
| PreferenceConstants.NAME_STYLE_GETTER_WORD_DELIMITER, "", null); //$NON-NLS-1$ |
| String prefix = bool |
| ? preferences.getString(CUIPlugin.PLUGIN_ID, PreferenceConstants.NAME_STYLE_GETTER_PREFIX_FOR_BOOLEAN, |
| "is", null) //$NON-NLS-1$ |
| : preferences.getString(CUIPlugin.PLUGIN_ID, PreferenceConstants.NAME_STYLE_GETTER_PREFIX, "get", null); //$NON-NLS-1$ |
| String suffix = preferences.getString(CUIPlugin.PLUGIN_ID, PreferenceConstants.NAME_STYLE_GETTER_SUFFIX, "", //$NON-NLS-1$ |
| null); |
| NameComposer composer = new NameComposer(capitalization, wordDelimiter, prefix, suffix); |
| return adjustName(composer.compose(baseName), excluded, context); |
| } |
| |
| /** |
| * Returns a suggested name for a setter that is guaranteed to be a valid identifier |
| * and not collide with a set of given names. |
| * |
| * @param baseName the name used as an inspiration |
| * @param excluded the set of excluded names, can be {@code null} |
| * @param context the translation unit for which the code is intended, can be {@code null} |
| * @return the suggested name, or {@code null} if all possible names are taken |
| */ |
| public static String suggestSetterName(String baseName, Set<String> excluded, ITranslationUnit context) { |
| IPreferencesService preferences = Platform.getPreferencesService(); |
| int capitalization = preferences.getInt(CUIPlugin.PLUGIN_ID, |
| PreferenceConstants.NAME_STYLE_SETTER_CAPITALIZATION, |
| PreferenceConstants.NAME_STYLE_CAPITALIZATION_CAMEL_CASE, null); |
| String wordDelimiter = preferences.getString(CUIPlugin.PLUGIN_ID, |
| PreferenceConstants.NAME_STYLE_SETTER_WORD_DELIMITER, "", null); //$NON-NLS-1$ |
| String prefix = preferences.getString(CUIPlugin.PLUGIN_ID, PreferenceConstants.NAME_STYLE_SETTER_PREFIX, "set", //$NON-NLS-1$ |
| null); |
| String suffix = preferences.getString(CUIPlugin.PLUGIN_ID, PreferenceConstants.NAME_STYLE_SETTER_SUFFIX, "", //$NON-NLS-1$ |
| null); |
| NameComposer composer = new NameComposer(capitalization, wordDelimiter, prefix, suffix); |
| return adjustName(composer.compose(baseName), excluded, context); |
| } |
| |
| /** |
| * Returns a suggested name for a function parameter that is guaranteed to be a valid identifier |
| * and not collide with a set of given names. |
| * |
| * @param baseName the name used as an inspiration |
| * @param excluded the set of excluded names, can be {@code null} |
| * @param context the translation unit for which the code is intended, can be {@code null} |
| * @return the suggested name, or {@code null} if all possible names are taken |
| */ |
| public static String suggestParameterName(String baseName, Set<String> excluded, ITranslationUnit context) { |
| IPreferencesService preferences = Platform.getPreferencesService(); |
| int capitalization = preferences.getInt(CUIPlugin.PLUGIN_ID, |
| PreferenceConstants.NAME_STYLE_VARIABLE_CAPITALIZATION, |
| PreferenceConstants.NAME_STYLE_CAPITALIZATION_ORIGINAL, null); |
| String wordDelimiter = preferences.getString(CUIPlugin.PLUGIN_ID, |
| PreferenceConstants.NAME_STYLE_VARIABLE_WORD_DELIMITER, "", null); //$NON-NLS-1$ |
| String prefix = preferences.getString(CUIPlugin.PLUGIN_ID, PreferenceConstants.NAME_STYLE_VARIABLE_PREFIX, "", //$NON-NLS-1$ |
| null); |
| String suffix = preferences.getString(CUIPlugin.PLUGIN_ID, PreferenceConstants.NAME_STYLE_VARIABLE_SUFFIX, "", //$NON-NLS-1$ |
| null); |
| NameComposer composer = new NameComposer(capitalization, wordDelimiter, prefix, suffix); |
| return adjustName(composer.compose(baseName), excluded, context); |
| } |
| |
| /** |
| * Returns a suggested name for a method that is guaranteed to be a valid identifier |
| * and not collide with a set of given names. |
| * |
| * @param baseName the name used as an inspiration |
| * @param excluded the set of excluded names, can be {@code null} |
| * @param context the translation unit for which the code is intended, can be {@code null} |
| * @return the suggested name, or {@code null} if all possible names are taken |
| */ |
| public static String suggestMethodName(String baseName, Set<String> excluded, ITranslationUnit context) { |
| IPreferencesService preferences = Platform.getPreferencesService(); |
| int capitalization = preferences.getInt(CUIPlugin.PLUGIN_ID, |
| PreferenceConstants.NAME_STYLE_METHOD_CAPITALIZATION, |
| PreferenceConstants.NAME_STYLE_CAPITALIZATION_ORIGINAL, null); |
| String wordDelimiter = preferences.getString(CUIPlugin.PLUGIN_ID, |
| PreferenceConstants.NAME_STYLE_METHOD_WORD_DELIMITER, "", null); //$NON-NLS-1$ |
| String prefix = preferences.getString(CUIPlugin.PLUGIN_ID, PreferenceConstants.NAME_STYLE_METHOD_PREFIX, "", //$NON-NLS-1$ |
| null); |
| String suffix = preferences.getString(CUIPlugin.PLUGIN_ID, PreferenceConstants.NAME_STYLE_METHOD_SUFFIX, "", //$NON-NLS-1$ |
| null); |
| NameComposer composer = new NameComposer(capitalization, wordDelimiter, prefix, suffix); |
| return adjustName(composer.compose(baseName), excluded, context); |
| } |
| |
| /** |
| * Checks is the given name is valid and, if not, tries to adjust it by adding a numeric suffix |
| * to it. |
| * |
| * @param name the name to check and, possibly, adjust |
| * @param namesToAvoid the set of names to avoid |
| * @param context the translation unit, can be {@code null} |
| * @return the adjusted name, or <code>null</code> if a valid name could not be generated. |
| */ |
| private static String adjustName(String name, Set<String> namesToAvoid, ITranslationUnit context) { |
| ILanguage language = null; |
| try { |
| if (context != null) |
| language = context.getLanguage(); |
| } catch (CoreException e) { |
| // Ignore |
| } |
| return adjustName(name, namesToAvoid, language); |
| } |
| |
| /** |
| * Checks is the given name is valid and, if not, tries to adjust it by adding a numeric suffix |
| * to it. |
| * |
| * @param name the name to check and, possibly, adjust |
| * @param namesToAvoid the set of names to avoid |
| * @param language the language of the translation unit, can be {@code null} |
| * @return the adjusted name, or <code>null</code> if a valid name could not be generated. |
| */ |
| private static String adjustName(String name, Set<String> namesToAvoid, ILanguage language) { |
| if (language == null) { |
| language = GPPLanguage.getDefault(); |
| } |
| String originalName = name; |
| if (!isValidIdentifier(name, language)) { |
| if ("class".equals(name)) { //$NON-NLS-1$ |
| name = "clazz"; //$NON-NLS-1$ |
| } else { |
| name = '_' + name; |
| } |
| } |
| int numTries = namesToAvoid != null ? namesToAvoid.size() + 1 : 1; |
| for (int i = 1; i <= numTries; i++) { |
| if ((namesToAvoid == null || !namesToAvoid.contains(name)) && isValidIdentifier(name, language)) { |
| return name; |
| } |
| name = originalName + i; |
| } |
| return null; |
| } |
| |
| private static boolean isValidIdentifier(String name, ILanguage language) { |
| if (language instanceof AbstractCLikeLanguage) { |
| return CConventions.validateIdentifier(name, (AbstractCLikeLanguage) language).isOK(); |
| } |
| return true; |
| } |
| |
| /** |
| * Returns the trimmed field name. Leading and trailing non-alphanumeric characters are trimmed. |
| * If the first word of the name consists of a single letter and the name contains more than |
| * one word, the first word is removed. |
| * |
| * @param fieldName a field name to trim |
| * @return the trimmed field name |
| */ |
| public static String trimFieldName(String fieldName) { |
| CBreakIterator iterator = new CBreakIterator(); |
| iterator.setText(fieldName); |
| int firstWordStart = -1; |
| int firstWordEnd = -1; |
| int secondWordStart = -1; |
| int lastWordEnd = -1; |
| int end; |
| for (int start = iterator.first(); (end = iterator.next()) != BreakIterator.DONE; start = end) { |
| if (Character.isLetterOrDigit(fieldName.charAt(start))) { |
| int pos = end; |
| while (--pos >= start && !Character.isLetterOrDigit(fieldName.charAt(pos))) { |
| } |
| lastWordEnd = pos + 1; |
| if (firstWordStart < 0) { |
| firstWordStart = start; |
| firstWordEnd = lastWordEnd; |
| } else if (secondWordStart < 0) { |
| secondWordStart = start; |
| } |
| } |
| } |
| // Skip the first word if it consists of a single letter and the name contains more than |
| // one word. |
| if (firstWordStart >= 0 && firstWordStart + 1 == firstWordEnd && secondWordStart >= 0) { |
| firstWordStart = secondWordStart; |
| } |
| if (firstWordStart < 0) { |
| return fieldName; |
| } else { |
| return fieldName.substring(firstWordStart, lastWordEnd); |
| } |
| } |
| } |