Skip to main content
summaryrefslogtreecommitdiffstats
blob: 96c26effe55e90fbd81f69042e80789e36f848cc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/*******************************************************************************
 * Copyright (c) 2000, 2016 IBM Corporation and others.
 * 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
 * $Id: CreateTypeMemberOperation.java 23401 2010-02-02 23:56:05Z stephan $
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Fraunhofer FIRST - extended API and implementation
 *     Technical University Berlin - extended API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.core;

import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelStatus;
import org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.RoleTypeDeclaration;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.formatter.IndentManipulation;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
import org.eclipse.objectteams.otdt.core.OTModelManager;

/**
 * Implements functionality common to
 * operations that create type members.
 */
@SuppressWarnings("rawtypes")
public abstract class CreateTypeMemberOperation extends CreateElementInCUOperation {
	/**
	 * The source code for the new member.
	 */
	protected String source = null;
	/**
	 * The name of the <code>ASTNode</code> that may be used to
	 * create this new element.
	 * Used by the <code>CopyElementsOperation</code> for renaming
	 */
	protected String alteredName;
	/**
	 * The AST node representing the element that
	 * this operation created.
	 */
	 protected ASTNode createdNode;
/**
 * When executed, this operation will create a type member
 * in the given parent element with the specified source.
 */
public CreateTypeMemberOperation(IJavaElement parentElement, String source, boolean force) {
	super(parentElement);
	this.source = source;
	this.force = force;
}
protected StructuralPropertyDescriptor getChildPropertyDescriptor(ASTNode parent) {
	switch (parent.getNodeType()) {
		case ASTNode.COMPILATION_UNIT:
			return CompilationUnit.TYPES_PROPERTY;
		case ASTNode.ENUM_DECLARATION:
			return EnumDeclaration.BODY_DECLARATIONS_PROPERTY;
		case ASTNode.ANNOTATION_TYPE_DECLARATION:
			return AnnotationTypeDeclaration.BODY_DECLARATIONS_PROPERTY;
//{ObjectTeams: RoleTypes:
		case ASTNode.ROLE_TYPE_DECLARATION:
			return RoleTypeDeclaration.BODY_DECLARATIONS_PROPERTY;
// SH}
		default:
			return TypeDeclaration.BODY_DECLARATIONS_PROPERTY;
	}
}
//{ObjectTeams: using raw options map:
@SuppressWarnings("unchecked")
// SH}
protected ASTNode generateElementAST(ASTRewrite rewriter, ICompilationUnit cu) throws JavaModelException {
	if (this.createdNode == null) {
		this.source = removeIndentAndNewLines(this.source, cu);
		ASTParser parser = ASTParser.newParser(AST.JLS9);
		parser.setSource(this.source.toCharArray());
		parser.setProject(getCompilationUnit().getJavaProject());
		parser.setKind(ASTParser.K_CLASS_BODY_DECLARATIONS);
//{ObjectTeams: check if parent is a team -> force parsing as OT/J?
		if (   (this.parentElements[0] instanceof IType)
			&& OTModelManager.hasOTElementFor((IType) this.parentElements[0])) {
			Map options = new Hashtable(getCompilationUnit().getJavaProject().getOptions(true));
			options.put(CompilerOptions.OPTION_AllowScopedKeywords, CompilerOptions.DISABLED);
			parser.setCompilerOptions(options);
		}
// SH}
		ASTNode node = parser.createAST(this.progressMonitor);
		String createdNodeSource;
//{ObjectTeams: role types:
/* orig:
		if (node.getNodeType() != ASTNode.TYPE_DECLARATION) {
  :giro */
		if (node.getNodeType() != ASTNode.TYPE_DECLARATION && node.getNodeType() != ASTNode.ROLE_TYPE_DECLARATION) {
// SH}
			createdNodeSource = generateSyntaxIncorrectAST();
			if (this.createdNode == null)
				throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_CONTENTS));
		} else {
			TypeDeclaration typeDeclaration = (TypeDeclaration) node;
			if ((typeDeclaration.getFlags() & ASTNode.MALFORMED) != 0) {
				createdNodeSource = generateSyntaxIncorrectAST();
				if (this.createdNode == null)
					throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_CONTENTS));
			} else {
				List bodyDeclarations = typeDeclaration.bodyDeclarations();
				if (bodyDeclarations.size() == 0) {
					throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_CONTENTS));
				}
				this.createdNode = (ASTNode) bodyDeclarations.iterator().next();
				createdNodeSource = this.source;
			}
		}
		if (this.alteredName != null) {
			SimpleName newName = this.createdNode.getAST().newSimpleName(this.alteredName);
			SimpleName oldName = rename(this.createdNode, newName);
			int nameStart = oldName.getStartPosition();
			int nameEnd = nameStart + oldName.getLength();
			StringBuffer newSource = new StringBuffer();
			if (this.source.equals(createdNodeSource)) {
				newSource.append(createdNodeSource.substring(0, nameStart));
				newSource.append(this.alteredName);
				newSource.append(createdNodeSource.substring(nameEnd));
			} else {
				// syntactically incorrect source
				int createdNodeStart = this.createdNode.getStartPosition();
				int createdNodeEnd = createdNodeStart + this.createdNode.getLength();
				newSource.append(createdNodeSource.substring(createdNodeStart, nameStart));
				newSource.append(this.alteredName);
				newSource.append(createdNodeSource.substring(nameEnd, createdNodeEnd));

			}
			this.source = newSource.toString();
		}
	}
	if (rewriter == null) return this.createdNode;
	// return a string place holder (instead of the created node) so has to not lose comments and formatting
	return rewriter.createStringPlaceholder(this.source, this.createdNode.getNodeType());
}
private String removeIndentAndNewLines(String code, ICompilationUnit cu) throws JavaModelException {
	IJavaProject project = cu.getJavaProject();
	Map options = project.getOptions(true/*inherit JavaCore options*/);
	int tabWidth = IndentManipulation.getTabWidth(options);
	int indentWidth = IndentManipulation.getIndentWidth(options);
	int indent = IndentManipulation.measureIndentUnits(code, tabWidth, indentWidth);
	int firstNonWhiteSpace = -1;
	int length = code.length();
	while (firstNonWhiteSpace < length-1)
		if (!ScannerHelper.isWhitespace(code.charAt(++firstNonWhiteSpace)))
			break;
	int lastNonWhiteSpace = length;
	while (lastNonWhiteSpace > 0)
		if (!ScannerHelper.isWhitespace(code.charAt(--lastNonWhiteSpace)))
			break;
	String lineDelimiter = cu.findRecommendedLineSeparator();
	return IndentManipulation.changeIndent(code.substring(firstNonWhiteSpace, lastNonWhiteSpace+1), indent, tabWidth, indentWidth, "", lineDelimiter); //$NON-NLS-1$
}
/*
 * Renames the given node to the given name.
 * Returns the old name.
 */
protected abstract SimpleName rename(ASTNode node, SimpleName newName);
/**
 * Generates an <code>ASTNode</code> based on the source of this operation
 * when there is likely a syntax error in the source.
 * Returns the source used to generate this node.
 */
protected String generateSyntaxIncorrectAST() {
	//create some dummy source to generate an ast node
	StringBuffer buff = new StringBuffer();
	IType type = getType();
	String lineSeparator = org.eclipse.jdt.internal.core.util.Util.getLineSeparator(this.source, type == null ? null : type.getJavaProject());
	buff.append(lineSeparator + " public class A {" + lineSeparator); //$NON-NLS-1$
	buff.append(this.source);
	buff.append(lineSeparator).append('}');
	ASTParser parser = ASTParser.newParser(AST.JLS9);
	parser.setSource(buff.toString().toCharArray());
	CompilationUnit compilationUnit = (CompilationUnit) parser.createAST(null);
	TypeDeclaration typeDeclaration = (TypeDeclaration) compilationUnit.types().iterator().next();
	List bodyDeclarations = typeDeclaration.bodyDeclarations();
	if (bodyDeclarations.size() != 0)
		this.createdNode = (ASTNode) bodyDeclarations.iterator().next();
	return buff.toString();
}
/**
 * Returns the IType the member is to be created in.
 */
protected IType getType() {
	return (IType)getParentElement();
}
/**
 * Sets the name of the <code>ASTNode</code> that will be used to
 * create this new element.
 * Used by the <code>CopyElementsOperation</code> for renaming
 */
protected void setAlteredName(String newName) {
	this.alteredName = newName;
}
/**
 * Possible failures: <ul>
 *  <li>NO_ELEMENTS_TO_PROCESS - the parent element supplied to the operation is
 * 		<code>null</code>.
 *	<li>INVALID_CONTENTS - The source is <code>null</code> or has serious syntax errors.
  *	<li>NAME_COLLISION - A name collision occurred in the destination
 * </ul>
 */
public IJavaModelStatus verify() {
	IJavaModelStatus status = super.verify();
	if (!status.isOK()) {
		return status;
	}
	if (this.source == null) {
		return new JavaModelStatus(IJavaModelStatusConstants.INVALID_CONTENTS);
	}
	if (!this.force) {
		//check for name collisions
		try {
			ICompilationUnit cu = getCompilationUnit();
			generateElementAST(null, cu);
		} catch (JavaModelException jme) {
			return jme.getJavaModelStatus();
		}
		return verifyNameCollision();
	}

	return JavaModelStatus.VERIFIED_OK;
}
/**
 * Verify for a name collision in the destination container.
 */
protected IJavaModelStatus verifyNameCollision() {
	return JavaModelStatus.VERIFIED_OK;
}
}

Back to the top