/*
* Copyright 2010 Jean-Paul Balabanian and Yngve Devik Hammersland
*
* This file is part of glsl4idea.
*
* Glsl4idea is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Glsl4idea is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with glsl4idea. If not, see <http://www.gnu.org/licenses/>.
*/
package glslplugin.lang.elements.declarations;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNameIdentifierOwner;
import com.intellij.psi.PsiReference;
import com.intellij.util.IncorrectOperationException;
import glslplugin.lang.elements.GLSLElementImpl;
import glslplugin.lang.elements.GLSLIdentifier;
import glslplugin.lang.elements.GLSLReferenceElement;
import glslplugin.lang.elements.expressions.GLSLExpression;
import glslplugin.lang.elements.reference.GLSLVariableReference;
import glslplugin.lang.elements.types.GLSLArrayType;
import glslplugin.lang.elements.types.GLSLQualifiedType;
import glslplugin.lang.elements.types.GLSLType;
import glslplugin.lang.elements.types.GLSLTypes;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* GLSLDeclarator represents a local or global variable declaration.
* It may contain one or more declarators.
*
* @author Yngve Devik Hammersland
* Date: Jan 29, 2009
* Time: 7:29:46 PM
*/
public class GLSLDeclarator extends GLSLElementImpl implements PsiNameIdentifierOwner, GLSLReferenceElement {
public static final GLSLDeclarator[] NO_DECLARATORS = new GLSLDeclarator[0];
public GLSLDeclarator(@NotNull ASTNode astNode) {
super(astNode);
}
@Nullable
public GLSLExpression getInitializerExpression() {
final GLSLInitializerExpression init = findChildByClass(GLSLInitializerExpression.class);
if (init != null) {
return init.getInitializerExpression();
} else {
return null;
}
}
@Nullable
public GLSLInitializer getInitializer(){
return findChildByClass(GLSLInitializer.class);
}
@Override
@Nullable
public GLSLIdentifier getNameIdentifier() {
PsiElement idElement = getFirstChild();
if (idElement instanceof GLSLIdentifier) {
return (GLSLIdentifier) idElement;
} else {
return null; //May trigger on malformed code
}
}
@NotNull
public String getName() {
GLSLIdentifier identifier = getNameIdentifier();
if (identifier != null) {
return identifier.getName();
} else {
return "(anonymous)";
}
}
@Override
public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
GLSLIdentifier identifier = getNameIdentifier();
if (identifier != null) {
return identifier.setName(name);
} else {
throw new IncorrectOperationException("Declarator with no name!");
}
}
@Nullable
public GLSLDeclaration getParentDeclaration() {
return findParentByClass(GLSLDeclarationImpl.class);
}
/**
* Clarifies array dimensions if this defines implicitly sized array by initializer.
* Does nothing otherwise.
*/
private GLSLType clarifyArrayType(GLSLType baseType){
if(!(baseType instanceof GLSLArrayType))return baseType; //No need to clarify non-array types
final GLSLArrayType myArrayType = (GLSLArrayType) baseType;
final int[] myDimensions = myArrayType.getDimensions();
{ //Try clarifying using initializer list
GLSLInitializer rawInitializer = getInitializer();
if (rawInitializer != null && rawInitializer instanceof GLSLInitializerList) {
//Clarify using initializer list
GLSLInitializerList initializerList = (GLSLInitializerList) rawInitializer;
for (int i = 0; i < myDimensions.length; i++) {
GLSLInitializer[] initializers = initializerList.getInitializers();
if (myDimensions[i] == GLSLArrayType.UNDEFINED_SIZE_DIMENSION) {
//Can clarify that!
myDimensions[i] = initializers.length;
}
if (initializers.length >= 1 && initializers[0] instanceof GLSLInitializerList) {
initializerList = (GLSLInitializerList) initializers[0];
} else {
//Can't clarify any more
break;
}
}
return baseType;//Dimensions are changed in place, so no need to create new instance
}
}
{ //Try clarifying using expression
GLSLExpression rawExpression = getInitializerExpression();
if(rawExpression != null){
GLSLType type = rawExpression.getType();
if(type instanceof GLSLArrayType){
GLSLArrayType arrayType = (GLSLArrayType) type;
//Great, it is being correctly initialized, try to copy as many missing dimensions as we can
final int[] dimensions = arrayType.getDimensions();
for (int i = 0; i < myDimensions.length && i < dimensions.length; i++) {
if(myDimensions[i] == GLSLArrayType.UNDEFINED_SIZE_DIMENSION){
//Copy that
myDimensions[i] = dimensions[i];
}
}
return baseType; //Dimensions are changed in place, so no need to create new instance
}//else - not even valid initializer
}
}
return baseType; //Could not clarify
}
@NotNull
public GLSLType getType() {
GLSLDeclaration declaration = getParentDeclaration();
if(declaration == null)return GLSLTypes.UNKNOWN_TYPE;
GLSLTypeSpecifier declarationType = declaration.getTypeSpecifierNode();
if(declarationType == null)return GLSLTypes.UNKNOWN_TYPE;
GLSLType declaredType = declarationType.getType();
if(!declaredType.isValidType())return GLSLTypes.UNKNOWN_TYPE;
GLSLArraySpecifier[] arraySpecifiers = findChildrenByClass(GLSLArraySpecifier.class);
if(arraySpecifiers.length == 0){
return clarifyArrayType(declaredType);
}else{
//Must _prepend_ some dimensions to the type
//In: "vec4[2][4] b[3]" b is "vec4[3][2][4]", not "vec4[2][4][3]"
if(declaredType instanceof GLSLArrayType){
//Already an array, must append the dimensions
GLSLArrayType declaredArrayType = (GLSLArrayType) declaredType;
int[] existingDimensions = declaredArrayType.getDimensions();
int[] combinedDimensions = new int[existingDimensions.length + arraySpecifiers.length];
System.arraycopy(existingDimensions, 0, combinedDimensions, arraySpecifiers.length, existingDimensions.length);
for (int i = 0; i < arraySpecifiers.length; i++) {
combinedDimensions[i] = arraySpecifiers[i].getDimensionSize();
}
return clarifyArrayType(new GLSLArrayType(declaredArrayType.getBaseType(), combinedDimensions));
}else{
int[] dimensions = new int[arraySpecifiers.length];
for (int i = 0; i < dimensions.length; i++) {
dimensions[i] = arraySpecifiers[i].getDimensionSize();
}
return clarifyArrayType(new GLSLArrayType(declaredType, dimensions));
}
}
}
@NotNull
public GLSLQualifiedType getQualifiedType() {
final GLSLType type = getType();
final GLSLDeclaration declaration = getParentDeclaration();
if(declaration == null || declaration.getQualifierList() == null)return new GLSLQualifiedType(type);
return new GLSLQualifiedType(type, declaration.getQualifierList().getQualifiers());
}
@Override
public String toString() {
return "Declarator: " + getName() + " : " + getType().getTypename();
}
@NotNull
@Override
public PsiReference getReferenceProxy() {
return new GLSLVariableReference(getNameIdentifier());
}
}