/*******************************************************************************
* Copyright (c) 2006, 2010 QNX Software Systems 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
*
* Contributors:
* QNX - Initial API and implementation
* Markus Schorn (Wind River Systems)
* Andrew Ferguson (Symbian)
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.core.pdom.dom;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IPDOMVisitor;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorUndefStatement;
import org.eclipse.cdt.core.dom.ast.IMacroBinding;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexMacro;
import org.eclipse.cdt.core.index.IndexLocationFactory;
import org.eclipse.cdt.core.parser.Keywords;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.index.IIndexBindingConstants;
import org.eclipse.cdt.internal.core.index.IIndexFragment;
import org.eclipse.cdt.internal.core.index.IIndexFragmentBinding;
import org.eclipse.cdt.internal.core.index.IIndexFragmentName;
import org.eclipse.cdt.internal.core.index.IIndexScope;
import org.eclipse.cdt.internal.core.parser.scanner.CharArray;
import org.eclipse.cdt.internal.core.parser.scanner.MacroDefinitionParser;
import org.eclipse.cdt.internal.core.pdom.PDOM;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.db.IString;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
/**
* Represents macro definitions. They are stored with the file and with a PDOMMacroContainer.
* The latter also contains the references to all macros with the same name.
*/
public class PDOMMacro implements IIndexMacro, IPDOMBinding, IASTFileLocation {
private static final int CONTAINER = 0;
private static final int FILE = 4;
private static final int PARAMETERS= 8;
private static final int EXPANSION = 12;
private static final int NEXT_IN_FILE = 16;
private static final int NEXT_IN_CONTAINER = 20;
private static final int PREV_IN_CONTAINER = 24;
private static final int NAME_OFFSET = 28;
private static final int NAME_LENGTH = 32; // short
private static final int RECORD_SIZE = 34;
private static final char[][] UNINITIALIZED= {};
private static final char[] UNINITIALIZED1= {};
private final PDOMLinkage fLinkage;
private final long fRecord;
private char[][] fParameterList= UNINITIALIZED;
private char[] fExpansion= UNINITIALIZED1;
private PDOMMacroContainer fContainer;
private PDOMMacroDefinitionName fDefinition;
public PDOMMacro(PDOMLinkage linkage, long record) {
fLinkage = linkage;
fRecord = record;
}
public PDOMMacro(PDOMLinkage linkage, PDOMMacroContainer container, IASTPreprocessorMacroDefinition macro,
PDOMFile file) throws CoreException {
this(linkage, container, file, macro.getName());
final IASTName name = macro.getName();
final IMacroBinding binding= (IMacroBinding) name.getBinding();
final char[][] params= binding.getParameterList();
final Database db= linkage.getDB();
db.putRecPtr(fRecord + EXPANSION, db.newString(binding.getExpansionImage()).getRecord());
if (params != null) {
StringBuilder buf= new StringBuilder();
for (char[] param : params) {
buf.append(param);
buf.append(',');
}
db.putRecPtr(fRecord + PARAMETERS, db.newString(buf.toString().toCharArray()).getRecord());
}
}
public PDOMMacro(PDOMLinkage linkage, PDOMMacroContainer container, IASTPreprocessorUndefStatement undef,
PDOMFile file) throws CoreException {
this(linkage, container, file, undef.getMacroName());
}
private PDOMMacro(PDOMLinkage linkage, PDOMMacroContainer container, PDOMFile file, IASTName name)
throws CoreException {
final Database db= linkage.getDB();
fLinkage = linkage;
fRecord = db.malloc(RECORD_SIZE);
fContainer= container;
final IASTFileLocation fileloc = name.getFileLocation();
db.putRecPtr(fRecord + CONTAINER, container.getRecord());
db.putRecPtr(fRecord + FILE, file.getRecord());
db.putInt(fRecord + NAME_OFFSET, fileloc.getNodeOffset());
db.putShort(fRecord + NAME_LENGTH, (short) fileloc.getNodeLength());
container.addDefinition(this);
}
public PDOM getPDOM() {
return fLinkage.getPDOM();
}
public long getRecord() {
return fRecord;
}
public void delete(PDOMLinkage linkage) throws CoreException {
// Delete from the binding chain
PDOMMacro prevName = getPrevInContainer();
PDOMMacro nextName = getNextInContainer();
if (prevName != null)
prevName.setNextInContainer(nextName);
else {
PDOMMacroContainer container= getContainer();
container.setFirstDefinition(nextName);
if (nextName == null && container.isOrphaned()) {
container.delete(linkage);
}
}
if (nextName != null)
nextName.setPrevInContainer(prevName);
final IString expansion = getExpansionInDB();
if (expansion != null) {
expansion.delete();
}
final IString params = getParamListInDB();
if (params != null) {
params.delete();
}
linkage.getDB().free(fRecord);
}
public PDOMMacroContainer getContainer() throws CoreException {
if (fContainer == null) {
fContainer= new PDOMMacroContainer(fLinkage, fLinkage.getDB().getRecPtr(fRecord + CONTAINER));
}
return fContainer;
}
private IString getExpansionInDB() throws CoreException {
Database db = fLinkage.getDB();
long rec = db.getRecPtr(fRecord + EXPANSION);
return rec == 0 ? null : db.getString(rec);
}
private IString getParamListInDB() throws CoreException {
Database db = fLinkage.getDB();
long rec = db.getRecPtr(fRecord + PARAMETERS);
return rec == 0 ? null : db.getString(rec);
}
public PDOMMacro getNextMacro() throws CoreException {
long rec = fLinkage.getDB().getRecPtr(fRecord + NEXT_IN_FILE);
return rec != 0 ? new PDOMMacro(fLinkage, rec) : null;
}
public void setNextMacro(PDOMMacro macro) throws CoreException {
setNextMacro(macro != null ? macro.getRecord() : 0);
}
private void setNextMacro(long rec) throws CoreException {
fLinkage.getDB().putRecPtr(fRecord + NEXT_IN_FILE, rec);
}
private PDOMMacro getPrevInContainer() throws CoreException {
return getMacroField(PREV_IN_CONTAINER);
}
void setPrevInContainer(PDOMMacro macro) throws CoreException {
setMacroField(PREV_IN_CONTAINER, macro);
}
public PDOMMacro getNextInContainer() throws CoreException {
return getMacroField(NEXT_IN_CONTAINER);
}
void setNextInContainer(PDOMMacro macro) throws CoreException {
setMacroField(NEXT_IN_CONTAINER, macro);
}
private void setMacroField(int offset, PDOMMacro macro) throws CoreException {
long namerec = macro != null ? macro.getRecord() : 0;
fLinkage.getDB().putRecPtr(fRecord + offset, namerec);
}
private PDOMMacro getMacroField(int offset) throws CoreException {
long namerec= fLinkage.getDB().getRecPtr(fRecord + offset);
return namerec != 0 ? new PDOMMacro(fLinkage, namerec) : null;
}
public char[][] getParameterList() {
if (fParameterList == UNINITIALIZED) {
fParameterList= null;
try {
IString plist= getParamListInDB();
if (plist != null) {
List<char[]> paramList = new ArrayList<char[]>();
final char[] cplist= plist.getChars();
final int end = cplist.length;
int from= 0;
int to= CharArrayUtils.indexOf(',', cplist, from, end);
while (to > from) {
paramList.add(CharArrayUtils.extract(cplist, from, to-from));
from= to+1;
to= CharArrayUtils.indexOf(',', cplist, from, end);
}
fParameterList= paramList.toArray(new char[paramList.size()][]);
}
} catch (CoreException e) {
CCorePlugin.log(e);
}
}
return fParameterList;
}
public boolean isMacroDefinition() throws CoreException {
if (fExpansion == UNINITIALIZED1) {
return fLinkage.getDB().getRecPtr(fRecord + EXPANSION) != 0;
}
return fExpansion != null;
}
public char[] getExpansionImage() {
if (fExpansion == UNINITIALIZED1) {
try {
final IString expansionInDB = getExpansionInDB();
fExpansion= expansionInDB == null ? null : expansionInDB.getChars();
} catch (CoreException e) {
CCorePlugin.log(e);
fExpansion= CharArrayUtils.EMPTY;
}
}
return fExpansion;
}
public char[] getNameCharArray() {
try {
return getContainer().getNameCharArray();
} catch (CoreException e) {
CCorePlugin.log(e);
return new char[] {' '};
}
}
public String getName() {
return new String(getNameCharArray());
}
public PDOMFile getFile() throws CoreException {
long filerec = fLinkage.getDB().getRecPtr(fRecord + FILE);
return filerec != 0 ? new PDOMFile(fLinkage, filerec) : null;
}
public long getFileRecord() throws CoreException {
return fLinkage.getDB().getRecPtr(fRecord + FILE);
}
void setFile(PDOMFile file) throws CoreException {
fLinkage.getDB().putRecPtr(fRecord + FILE, file != null ? file.getRecord() : 0);
}
public String getFileName() {
try {
IIndexFile file = getFile();
if (file == null) {
return null;
}
// We need to specify what this method can return to know
// how to implement this. Existing implementations return
// the absolute path, so here we attempt to do the same.
IPath location = IndexLocationFactory.getAbsolutePath(file.getLocation());
return location != null ? location.toOSString() : null;
} catch (CoreException e) {
CCorePlugin.log(e);
}
return null;
}
public int getStartingLineNumber() {
return 0;
}
public int getEndingLineNumber() {
return 0;
}
public IASTPreprocessorIncludeStatement getContextInclusionStatement() {
return null;
}
public IASTFileLocation asFileLocation() {
return this;
}
public IASTFileLocation getFileLocation() {
return this;
}
public int getNodeLength() {
try {
return fLinkage.getDB().getShort(fRecord + NAME_LENGTH);
} catch (CoreException e) {
CCorePlugin.log(e);
return 0;
}
}
public int getNodeOffset() {
try {
return fLinkage.getDB().getInt(fRecord + NAME_OFFSET);
} catch (CoreException e) {
CCorePlugin.log(e);
return 0;
}
}
public char[] getExpansion() {
char[] expansionImage= getExpansionImage();
return MacroDefinitionParser.getExpansion(new CharArray(expansionImage), 0, expansionImage.length);
}
public char[][] getParameterPlaceholderList() {
char[][] params= getParameterList();
if (params != null && params.length > 0) {
char[] lastParam= params[params.length-1];
if (CharArrayUtils.equals(lastParam, 0, Keywords.cpELLIPSIS.length, Keywords.cpELLIPSIS)) {
char[][] result= new char[params.length][];
System.arraycopy(params, 0, result, 0, params.length-1);
result[params.length-1]= lastParam.length == Keywords.cpELLIPSIS.length ? Keywords.cVA_ARGS :
CharArrayUtils.extract(lastParam, Keywords.cpELLIPSIS.length, lastParam.length-Keywords.cpELLIPSIS.length);
return result;
}
}
return params;
}
public boolean isFunctionStyle() {
return getParameterList() != null;
}
public boolean isDynamic() {
return false;
}
public PDOMLinkage getLinkage() {
return fLinkage;
}
public IIndexScope getScope() {
return null;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Object getAdapter(Class adapter) {
if (adapter.isAssignableFrom(PDOMMacro.class)) {
return this;
}
return null;
}
public IIndexFragmentName getDefinition() throws CoreException {
if (!isMacroDefinition()) {
return null;
}
if (fDefinition == null) {
fDefinition= new PDOMMacroDefinitionName(this);
}
return fDefinition;
}
public IIndexFile getLocalToFile() throws CoreException {
return null;
}
public String[] getQualifiedName() {
return new String[]{getName()};
}
public boolean isFileLocal() throws CoreException {
return false;
}
public int getBindingConstant() {
return IIndexBindingConstants.MACRO_DEFINITION;
}
public IIndexFragment getFragment() {
return fLinkage.getPDOM();
}
public boolean hasDeclaration() throws CoreException {
return false;
}
public boolean hasDefinition() throws CoreException {
return true;
}
public IIndexFragmentBinding getOwner() {
return null;
}
public void accept(IPDOMVisitor visitor) {
}
public long getBindingID() {
return fRecord;
}
}