/*******************************************************************************
* Copyright (c) 2004, 2011 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
*
* Contributors:
* IBM - Initial API and implementation
* Markus Schorn (Wind River Systems)
* Gerhard Schaber (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.make.internal.core.scannerconfig.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.cdt.internal.core.SafeStringInterner;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* Class that represents a compiler command and related scanner configuration
*
* @author vhirsl
*/
public class CCommandDSC {
protected final static String SINGLE_SPACE = " "; //$NON-NLS-1$
protected final static String CMD_DESCRIPTION_ELEM = "commandDescription"; //$NON-NLS-1$
protected final static String CMD_SI_ELEM = "commandScannerInfo"; //$NON-NLS-1$
protected final static String OPTION_ELEM = "option"; //$NON-NLS-1$
protected final static String SI_ITEM_ELEM = "siItem"; //$NON-NLS-1$
protected final static String KEY_ATTR = "key"; //$NON-NLS-1$
protected final static String VALUE_ATTR = "value"; //$NON-NLS-1$
protected final static String QUOTE_INCLUDE_ATTR = "quote"; //$NON-NLS-1$
protected final static String KIND_ATTR = "kind"; //$NON-NLS-1$
protected int commandId;
protected List<KVStringPair> compilerCommand; // members are KVStringPair objects
protected boolean discovered;
protected boolean cppFileType; // C or C++ file type
protected IProject project;
protected List<String> symbols;
protected List<String> includes;
protected List<String> quoteIncludes;
public CCommandDSC(boolean cppFileType) {
this(cppFileType, null);
}
public CCommandDSC(boolean cppFileType, IProject project) {
compilerCommand = new ArrayList<KVStringPair>();
discovered = false;
this.cppFileType = cppFileType;
symbols = new ArrayList<String>();
includes = new ArrayList<String>();
quoteIncludes = new ArrayList<String>();
this.project = project;
}
public boolean appliesToCPPFileType() {
return cppFileType;
}
public void addSCOption(KVStringPair option) {
if (project != null &&
(option.getKey().equals(SCDOptionsEnum.INCLUDE_FILE.toString()) ||
option.getKey().equals(SCDOptionsEnum.INCLUDE.toString()) ||
option.getKey().equals(SCDOptionsEnum.ISYSTEM.toString()) ||
option.getKey().equals(SCDOptionsEnum.IMACROS_FILE.toString()) ||
option.getKey().equals(SCDOptionsEnum.IQUOTE.toString())))
{
String value = option.getValue();
value = CygpathTranslator.translateIncludePaths(project, Collections.singletonList(value)).get(0);
value = SafeStringInterner.safeIntern(makeRelative(project, new Path(value)).toOSString());
option = new KVStringPair(option.getKey(), value);
}
compilerCommand.add(option);
}
public Integer getCommandIdAsInteger() {
return new Integer(getCommandId());
}
/**
* @return Returns the commandId.
*/
public int getCommandId() {
return commandId;
}
/**
* @param commandId The commandId to set.
*/
public void setCommandId(int commandId) {
this.commandId = commandId;
}
@Override
public String toString() {
String commandAsString = new String();
for (Iterator<KVStringPair> i = compilerCommand.iterator(); i.hasNext(); ) {
KVStringPair optionPair = i.next();
String value = optionPair.getValue();
commandAsString += optionPair.getKey() + SINGLE_SPACE +
value + SINGLE_SPACE;
}
return commandAsString.trim();
}
public int getId() {
return commandId;
}
/**
* Returns a command where -imacros and -include options have been removed
* @param quoteIncludePaths whether or not paths for includes must be put inside double quotes.
* @return the command line to run the scanner discovery.
*/
public String getSCDRunnableCommand(boolean quoteIncludePaths, boolean quoteDefines) {
String commandAsString = new String();
for (Iterator<KVStringPair> i = compilerCommand.iterator(); i.hasNext(); ) {
KVStringPair optionPair = i.next();
if (optionPair.getKey().equals(SCDOptionsEnum.COMMAND.toString())) {
commandAsString += optionPair.getValue() + SINGLE_SPACE;
}
else {
// skip -include and -imacros options
if (optionPair.getKey().equals(SCDOptionsEnum.IMACROS_FILE.toString()) ||
optionPair.getKey().equals(SCDOptionsEnum.INCLUDE_FILE.toString()))
continue;
String value = optionPair.getValue();
if (optionPair.getKey().equals(SCDOptionsEnum.INCLUDE.toString()) ||
optionPair.getKey().equals(SCDOptionsEnum.ISYSTEM.toString()) ||
optionPair.getKey().equals(SCDOptionsEnum.IQUOTE.toString())) {
value = makeAbsolute(project, value);
if (quoteIncludePaths) {
value= "\"" + value + "\""; //$NON-NLS-1$ //$NON-NLS-2$
}
}
else if (quoteDefines && optionPair.getKey().equals(SCDOptionsEnum.DEFINE.toString())) {
if (value.indexOf('\'') == -1) {
value= "'" + value + "'"; //$NON-NLS-1$//$NON-NLS-2$
}
else {
value= value.replaceAll("\"", "\\\\\""); //$NON-NLS-1$//$NON-NLS-2$
value= "\"" + value + "\""; //$NON-NLS-1$ //$NON-NLS-2$
}
}
commandAsString += optionPair.getKey() + SINGLE_SPACE + value + SINGLE_SPACE;
}
}
return commandAsString.trim();
}
/**
* @return the compiler command
*/
public String getCompilerName() {
String compiler = new String();
for (Iterator<KVStringPair> i = compilerCommand.iterator(); i.hasNext(); ) {
KVStringPair optionPair = i.next();
if (optionPair.getKey().equals(SCDOptionsEnum.COMMAND.toString())) {
compiler = optionPair.getValue();
break;
}
}
return compiler.trim();
}
/**
* @return list of strings
*/
public List<String> getImacrosFile() {
List<String> imacrosFiles = new ArrayList<String>();
for (Iterator<KVStringPair> i = compilerCommand.iterator(); i.hasNext(); ) {
KVStringPair optionPair = i.next();
if (optionPair.getKey().equals(SCDOptionsEnum.IMACROS_FILE.toString())) {
imacrosFiles.add(makeAbsolute(project,optionPair.getValue()));
}
}
return imacrosFiles;
}
/**
* @return list of strings
*/
public List<String> getIncludeFile() {
List<String> includeFiles = new ArrayList<String>();
for (Iterator<KVStringPair> i = compilerCommand.iterator(); i.hasNext(); ) {
KVStringPair optionPair = i.next();
if (optionPair.getKey().equals(SCDOptionsEnum.INCLUDE_FILE.toString())) {
includeFiles.add(makeAbsolute(project,optionPair.getValue()));
}
}
return includeFiles;
}
// public List getFilesList() {
// return files;
// }
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object arg0) {
if (arg0 != null && arg0.getClass().equals(this.getClass())) {
CCommandDSC other = (CCommandDSC)arg0;
return (compilerCommand.equals(other.compilerCommand) &&
cppFileType == other.cppFileType);
}
return false;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return compilerCommand.hashCode();
}
/**
* @return Returns the includes as strings.
*/
public List<String> getIncludes() {
return makeAbsolute(project, includes);
}
/**
* @param includes The includes to set.
*/
public void setIncludes(List<String> includes) {
this.includes = includes;
}
/**
* @return Returns the quote include paths as strings (for #include "...")
*/
public List<String> getQuoteIncludes() {
return makeAbsolute(project, quoteIncludes);
}
/**
* @param includes - quote include paths (for #include "...")
*/
public void setQuoteIncludes(List<String> includes) {
quoteIncludes = includes;
}
/**
* @return Returns the symbols.
*/
public List<String> getSymbols() {
return symbols;
}
/**
* @param symbols The symbols to set.
*/
public void setSymbols(List<String> symbols) {
this.symbols = symbols;
}
/**
* @return Returns the discovered.
*/
public boolean isDiscovered() {
return discovered;
}
/**
* @param discovered The discovered to set.
*/
public void setDiscovered(boolean discovered) {
this.discovered = discovered;
}
public void serialize(Element cmdElem) {
Document doc = cmdElem.getOwnerDocument();
// serialize the command
Element cmdDescElem = doc.createElement(CMD_DESCRIPTION_ELEM);
for (Iterator<KVStringPair> i = compilerCommand.iterator(); i.hasNext(); ) {
Element optionElem = doc.createElement(OPTION_ELEM);
KVStringPair option = i.next();
optionElem.setAttribute(KEY_ATTR, option.getKey());
optionElem.setAttribute(VALUE_ATTR, option.getValue());
cmdDescElem.appendChild(optionElem);
}
cmdElem.appendChild(cmdDescElem);
// serialize includes and symbols
Element siElem = doc.createElement(CMD_SI_ELEM);
for (Iterator<String> j = quoteIncludes.iterator(); j.hasNext(); ) {
Element siItem = doc.createElement(SI_ITEM_ELEM);
siItem.setAttribute(KIND_ATTR, "INCLUDE_PATH"); //$NON-NLS-1$
siItem.setAttribute(VALUE_ATTR, j.next());
siItem.setAttribute(QUOTE_INCLUDE_ATTR, "true"); //$NON-NLS-1$
siElem.appendChild(siItem);
}
for (Iterator<String> j = includes.iterator(); j.hasNext(); ) {
Element siItem = doc.createElement(SI_ITEM_ELEM);
siItem.setAttribute(KIND_ATTR, "INCLUDE_PATH"); //$NON-NLS-1$
siItem.setAttribute(VALUE_ATTR, j.next());
siElem.appendChild(siItem);
}
for (Iterator<String> j = symbols.iterator(); j.hasNext(); ) {
Element siItem = doc.createElement(SI_ITEM_ELEM);
siItem.setAttribute(KIND_ATTR, "SYMBOL_DEFINITION"); //$NON-NLS-1$
siItem.setAttribute(VALUE_ATTR, j.next());
siElem.appendChild(siItem);
}
cmdElem.appendChild(siElem);
}
public void deserialize(Element cmdElem) {
// read command options
NodeList descList = cmdElem.getElementsByTagName(CMD_DESCRIPTION_ELEM);
if (descList.getLength() > 0) {
Element descElem = (Element) descList.item(0);
NodeList optionList = descElem.getElementsByTagName(OPTION_ELEM);
for (int i = 0; i < optionList.getLength(); ++i) {
Element optionElem = (Element) optionList.item(i);
String key = SafeStringInterner.safeIntern(optionElem.getAttribute(KEY_ATTR));
String value = SafeStringInterner.safeIntern(optionElem.getAttribute(VALUE_ATTR));
KVStringPair option = new KVStringPair(key, value);
addSCOption(option);
}
}
// read associated scanner info
NodeList siList = cmdElem.getElementsByTagName(CMD_SI_ELEM);
if (siList.getLength() > 0) {
Element siElem = (Element) siList.item(0);
NodeList siItemList = siElem.getElementsByTagName(SI_ITEM_ELEM);
for (int i = 0; i < siItemList.getLength(); ++i) {
Element siItemElem = (Element) siItemList.item(i);
String kind = siItemElem.getAttribute(KIND_ATTR);
String value = siItemElem.getAttribute(VALUE_ATTR);
String quote = siItemElem.getAttribute(QUOTE_INCLUDE_ATTR);
if (kind.equals("INCLUDE_PATH")) { //$NON-NLS-1$
if (quote.equals("true")) { //$NON-NLS-1$
quoteIncludes.add(SafeStringInterner.safeIntern(value));
}
else {
includes.add(value);
}
}
else if (kind.equals("SYMBOL_DEFINITION")) { //$NON-NLS-1$
symbols.add(SafeStringInterner.safeIntern(value));
}
}
setDiscovered(true);
}
}
public void resolveOptions(IProject project) {
if (!isDiscovered()) {
// that's wrong for sure, options cannot be resolved fron the optionPairs??
ArrayList<String> symbols = new ArrayList<String>();
ArrayList<String> includes = new ArrayList<String>();
ArrayList<String> quoteincludes = new ArrayList<String>();
for (Iterator<KVStringPair> options = compilerCommand.iterator(); options.hasNext(); ) {
KVStringPair optionPair = options.next();
String key = optionPair.getKey();
String value = optionPair.getValue();
if (key.equals(SCDOptionsEnum.INCLUDE.toString()) || key.equals(SCDOptionsEnum.ISYSTEM.toString())) {
includes.add(SafeStringInterner.safeIntern(value));
}
else if (key.equals(SCDOptionsEnum.IQUOTE.toString())) {
quoteincludes.add(SafeStringInterner.safeIntern(value));
}
else if (key.equals(SCDOptionsEnum.DEFINE.toString())) {
symbols.add(SafeStringInterner.safeIntern(value));
}
}
setIncludes(includes);
setQuoteIncludes(quoteincludes);
setSymbols(symbols);
}
setDiscovered(true);
}
public static IPath makeRelative(IProject project, IPath path) {
IResource resource = findResource(project, path);
if (resource != null) {
if (resource.getProject() == project) {
path = resource.getProjectRelativePath();
}
// else {
// path = resource.getFullPath();
// }
}
return path;
}
protected static IResource findResource(IProject project, IPath path) {
IResource resource = project.findMember(path, false);
if (resource == null) {
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
resource = root.findMember(path, false);
if (resource == null) {
resource= ResourceLookup.selectFileForLocation(path, project);
}
}
return resource;
}
public static List<String> makeRelative(IProject project, List<String> paths) {
List<String> list = new ArrayList<String>(paths.size());
for (Iterator<String> iter=paths.iterator(); iter.hasNext(); ) {
String path = iter.next();
path = makeRelative(project, new Path(path)).toOSString();
list.add(SafeStringInterner.safeIntern(path));
}
return list;
}
public static final String makeAbsolute(IProject project, String path) {
IPath ppath = new Path(path);
if (project != null && !ppath.isAbsolute()) {
IResource res = project.findMember(ppath);
if (res == null) {
// To calculate path only; this does not create any file
res = project.getFile(path);
}
if (res != null) {
ppath = res.getLocation();
if (ppath != null) {
path = ppath.toOSString();
}
}
// path = new File(project.getLocation().toOSString(), path).getAbsolutePath();
}
return SafeStringInterner.safeIntern(path);
}
public static List<String> makeAbsolute(IProject project, List<String> paths) {
List<String> list = new ArrayList<String>(paths.size());
for (Iterator<String> iter=paths.iterator(); iter.hasNext(); ) {
String path = iter.next();
path = makeAbsolute(project, path);
list.add(SafeStringInterner.safeIntern(path));
}
return list;
}
}