blob: 327cab63b8e1f71a50f5260d066edf4f712b6437 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.javascript2.model;
import org.netbeans.modules.javascript2.model.api.ModelUtils;
import com.oracle.js.parser.ir.FunctionNode;
import com.oracle.js.parser.ir.IdentNode;
import com.oracle.js.parser.ir.LiteralNode;
import com.oracle.js.parser.ir.ObjectNode;
import com.oracle.js.parser.ir.PropertyNode;
import com.oracle.js.parser.Token;
import java.util.ArrayList;
import java.util.List;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.modules.csl.api.Modifier;
import org.netbeans.modules.csl.api.OffsetRange;
import org.netbeans.modules.javascript2.doc.api.JsDocumentationSupport;
import org.netbeans.modules.javascript2.doc.spi.JsDocumentationHolder;
import org.netbeans.modules.javascript2.model.api.JsFunction;
import org.netbeans.modules.javascript2.model.api.JsObject;
import org.netbeans.modules.javascript2.model.api.Occurrence;
import org.netbeans.modules.javascript2.types.api.Identifier;
import org.netbeans.modules.javascript2.types.spi.ParserResult;
/**
*
* @author Petr Pisl
*/
class ModelElementFactory {
@CheckForNull
static JsFunctionImpl create(ParserResult parserResult, FunctionNode functionNode, List<Identifier> fqName, ModelBuilder modelBuilder, boolean isAnnonymous, JsObject parent) {
if (EmbeddingHelper.containsGeneratedIdentifier(fqName.get(fqName.size() - 1).getName())) {
return null;
}
JsObjectImpl inObject = modelBuilder.getCurrentObject();
JsObject globalObject = modelBuilder.getGlobal();
JsObject parentObject = parent;
if (parent == null) {
if (isAnnonymous) {
DeclarationScopeImpl decScope = modelBuilder.getCurrentDeclarationScope();
// while (decScope != null && decScope.isAnonymous()) {
// decScope = (DeclarationScopeImpl)decScope.getParentScope();
// }
parentObject = decScope == null ? globalObject : decScope;
} else {
parentObject = inObject;
}
while(parentObject.getParent() != null && parentObject.getModifiers().contains(Modifier.PROTECTED)) {
parentObject = parentObject.getParent();
}
}
int start = Token.descPosition(functionNode.getFirstToken());
int end = Token.descPosition(functionNode.getLastToken()) + Token.descLength(functionNode.getLastToken());
if (end <= start) {
end = start + 1;
assert false: "The end offset of a function is before the start offset: [" + start + ", " + end + "] in file: " + parserResult.getSnapshot().getSource().getFileObject().getPath(); //NOI18N
}
List<Identifier> parameters = new ArrayList<>(functionNode.getParameters().size());
for(IdentNode node: functionNode.getParameters()) {
Identifier param = create(parserResult, node);
if (param != null) {
// can be null, if it's a generated embeding.
parameters.add(param);
}
}
JsFunctionImpl result;
if (fqName.size() > 1) {
List<Identifier> objectName = fqName.subList(0, fqName.size() - 1);
parentObject = isAnnonymous ? globalObject : ModelUtils.getJsObject(modelBuilder, objectName, false);
result = new JsFunctionImpl(modelBuilder.getCurrentDeclarationFunction(),
parentObject, fqName.get(fqName.size() - 1), parameters,
new OffsetRange(start, end), parserResult.getSnapshot().getMimeType(), null);
if (parentObject instanceof JsFunction && !ModelUtils.PROTOTYPE.equals(parentObject.getName())) {
result.addModifier(Modifier.STATIC);
}
} else {
result = new JsFunctionImpl(modelBuilder.getCurrentDeclarationFunction(),
parentObject, fqName.get(fqName.size() - 1), parameters,
new OffsetRange(start, end), parserResult.getSnapshot().getMimeType(), null);
}
String propertyName = result.getDeclarationName().getName();
if (parentObject == null) {
parentObject = globalObject;
}
JsObject property = parentObject.getProperty(propertyName); // the already existing property
parentObject.addProperty(result.getDeclarationName().getName(), result);
if (property != null) {
if (property.getDeclarationName() != null) {
result.addOccurrence(property.getDeclarationName().getOffsetRange());
}
for(Occurrence occurrence : property.getOccurrences()) {
result.addOccurrence(occurrence.getOffsetRange());
}
}
JsDocumentationHolder docHolder = JsDocumentationSupport.getDocumentationHolder(parserResult);
if (docHolder != null) {
result.setDocumentation(docHolder.getDocumentation(functionNode));
}
result.setAnonymous(isAnnonymous);
return result;
}
@NonNull
static JsFunctionImpl createVirtualFunction(ParserResult parserResult, JsObject parentObject, Identifier name, int paramCount) {
List<Identifier> params = new ArrayList<Identifier>(paramCount);
if (paramCount == 1) {
params.add(new Identifier("param", OffsetRange.NONE));
} else {
for(int i = 0; i < paramCount; i++) {
params.add(new Identifier("param" + (i + 1), OffsetRange.NONE));
}
}
JsFunctionImpl virtual = new JsFunctionImpl(parserResult.getSnapshot().getSource().getFileObject(),
parentObject, name, params, parserResult.getSnapshot().getMimeType(), null);
if (virtual.hasExactName()) {
virtual.addOccurrence(name.getOffsetRange());
}
return virtual;
}
@CheckForNull
static Identifier create(ParserResult parserResult, IdentNode node) {
return create(parserResult, node.getName(), node.getStart(), node.getFinish());
}
@CheckForNull
static Identifier create(ParserResult parserResult, LiteralNode node) {
return create(parserResult, node.getString(), node.getStart(), node.getFinish());
}
@CheckForNull
static Identifier create(ParserResult parserResult, String name, int start, int end) {
if (EmbeddingHelper.containsGeneratedIdentifier(name)) {
return null;
}
return new Identifier(name, new OffsetRange(start, end));
}
@CheckForNull
static JsObjectImpl create(ParserResult parserResult, ObjectNode objectNode, List<Identifier> fqName, ModelBuilder modelBuilder, boolean belongsToParent) {
if (EmbeddingHelper.containsGeneratedIdentifier(fqName.get(fqName.size() - 1).getName())) {
return null;
}
JsObjectImpl scope = modelBuilder.getCurrentObject();
JsObject parent = scope;
JsObject result = null;
Identifier name = fqName.get(fqName.size() - 1);
JsObjectImpl newObject;
if (!belongsToParent) {
List<Identifier> objectName = fqName.size() > 1 ? fqName.subList(0, fqName.size() - 1) : fqName;
parent = ModelUtils.getJsObject(modelBuilder, objectName, false);
if (parent != null) {
parent = parent.getParent();
}
if (parent == null) {
parent = modelBuilder.getGlobal();
}
}
result = parent.getProperty(name.getName());
newObject = new JsObjectImpl(parent, name, new OffsetRange(objectNode.getStart(), objectNode.getFinish()),
parserResult.getSnapshot().getMimeType(), null);
newObject.setDeclared(true);
if (result != null) {
// the object already exist due a definition of a property => needs to be copied
for (String propertyName : result.getProperties().keySet()) {
newObject.addProperty(propertyName, result.getProperty(propertyName));
}
}
JsDocumentationHolder docHolder = JsDocumentationSupport.getDocumentationHolder(parserResult);
if (docHolder != null) {
newObject.setDeprecated(docHolder.isDeprecated(objectNode));
newObject.setDocumentation(docHolder.getDocumentation(objectNode));
}
parent.addProperty(name.getName(), newObject);
if (newObject.hasExactName()) {
newObject.addOccurrence(newObject.getDeclarationName().getOffsetRange());
}
return newObject;
}
@CheckForNull
static JsArrayImpl create(ParserResult parserResult, LiteralNode.ArrayLiteralNode aNode, List<Identifier> fqName, ModelBuilder modelBuilder, boolean belongsToParent, JsObject suggestedParent) {
if (EmbeddingHelper.containsGeneratedIdentifier(fqName.get(fqName.size() - 1).getName())) {
return null;
}
JsObject parent = suggestedParent != null ? suggestedParent : modelBuilder.getCurrentObject();
JsObject result = null;
Identifier name = fqName.get(fqName.size() - 1);
JsArrayImpl newObject;
if (!belongsToParent) {
List<Identifier> objectName = fqName.size() > 1 ? fqName.subList(0, fqName.size() - 1) : fqName;
parent = ModelUtils.getJsObject(modelBuilder, objectName, false);
}
result = parent.getProperty(name.getName());
newObject = new JsArrayImpl(parent, name, new OffsetRange(aNode.getStart(), aNode.getFinish()),
parserResult.getSnapshot().getMimeType(), null);
newObject.setDeclared(true);
if (result != null) {
// the object already exist due a definition of a property => needs to be copied
for (String propertyName : result.getProperties().keySet()) {
newObject.addProperty(propertyName, result.getProperty(propertyName));
}
for (Occurrence occurence: result.getOccurrences()) {
newObject.addOccurrence(occurence.getOffsetRange());
}
if (result.isDeclared()) {
newObject.getModifiers().clear();
newObject.getModifiers().addAll(result.getModifiers());
newObject.setDeclarationName(result.getDeclarationName());
}
}
JsDocumentationHolder docHolder = JsDocumentationSupport.getDocumentationHolder(parserResult);
if (docHolder != null) {
newObject.setDeprecated(docHolder.isDeprecated(aNode));
newObject.setDocumentation(docHolder.getDocumentation(aNode));
}
parent.addProperty(name.getName(), newObject);
if (newObject.hasExactName() && newObject.getDeclarationName() != null) {
newObject.addOccurrence(newObject.getDeclarationName().getOffsetRange());
}
return newObject;
}
@NonNull
static JsObjectImpl createAnonymousObject(ParserResult parserResult, ObjectNode objectNode, ModelBuilder modelBuilder) {
String name = modelBuilder.getUnigueNameForAnonymObject(parserResult);
JsObjectImpl result = new AnonymousObject(modelBuilder.getCurrentDeclarationFunction(),
name, new OffsetRange(objectNode.getStart(), objectNode.getFinish()), parserResult.getSnapshot().getMimeType(), null);
modelBuilder.getCurrentDeclarationFunction().addProperty(name, result);
JsDocumentationHolder docHolder = JsDocumentationSupport.getDocumentationHolder(parserResult);
if (docHolder != null) {
result.setDocumentation(docHolder.getDocumentation(objectNode));
result.setDeprecated(docHolder.isDeprecated(objectNode));
}
return result;
}
@NonNull
static JsArrayImpl createAnonymousObject(ParserResult parserResult, LiteralNode.ArrayLiteralNode aNode, ModelBuilder modelBuilder) {
String name = modelBuilder.getUnigueNameForAnonymObject(parserResult);
JsArrayImpl result = new AnonymousObject.AnonymousArray(modelBuilder.getCurrentDeclarationFunction(),
name, new OffsetRange(aNode.getStart(), aNode.getFinish()), parserResult.getSnapshot().getMimeType(), null);
modelBuilder.getCurrentDeclarationFunction().addProperty(name, result);
JsDocumentationHolder docHolder = JsDocumentationSupport.getDocumentationHolder(parserResult);
if (docHolder != null) {
result.setDocumentation(docHolder.getDocumentation(aNode));
result.setDeprecated(docHolder.isDeprecated(aNode));
}
return result;
}
@CheckForNull
static JsObjectImpl create(ParserResult parserResult, PropertyNode propertyNode, Identifier name, ModelBuilder modelBuilder, boolean belongsToParent) {
if (EmbeddingHelper.containsGeneratedIdentifier(name.getName())) {
return null;
}
JsObjectImpl scope = modelBuilder.getCurrentObject();
JsObjectImpl property = new JsObjectImpl(scope, name, name.getOffsetRange(), parserResult.getSnapshot().getMimeType(), null);
JsDocumentationHolder docHolder = JsDocumentationSupport.getDocumentationHolder(parserResult);
property.setDocumentation(docHolder.getDocumentation(propertyNode));
property.setDeprecated(docHolder.isDeprecated(propertyNode));
if (property.hasExactName()) {
property.addOccurrence(property.getDeclarationName().getOffsetRange());
}
return property;
}
}