/*******************************************************************************
* Copyright (c) 2013, 2014 Red Hat, Inc.
* 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:
* Red Hat Inc. - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.thym.ios.core.pbxproject;
import java.io.File;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.HashMap;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.FilenameUtils;
import com.dd.plist.ASCIIPropertyListParser;
import com.dd.plist.NSArray;
import com.dd.plist.NSDictionary;
import com.dd.plist.NSObject;
import com.dd.plist.NSString;
import com.dd.plist.PropertyListParser;
public class PBXProject {
private static final String KEY_LIBRARY_SEARCH_PATHS = "LIBRARY_SEARCH_PATHS";
private final File file;
private NSDictionary root;
public PBXProject(File pbxfile){
this.file = pbxfile;
}
public void addSourceFile(PBXFile file) throws PBXProjectException{
addPluginFile(file);
addToPbxBuildFileSection(file); // PBXBuildFile
addToBuildPhase("PBXSourcesBuildPhase",file); // PBXSourcesBuildPhase
}
public void addPluginFile(PBXFile file) throws PBXProjectException{
file.setPlugin(true);
NSDictionary pluginsGroup= getGroupByName("Plugins");
if(pluginsGroup.containsKey("path")){
String path = file.getPath();
int index = path.indexOf("Plugins/");
if(index>-1){
file.setPath(path.substring(index+"Plugins/".length()));
}
}
this.addToPbxFileReferenceSection(file); // PBXFileReference
this.addToPbxGroup("Plugins",file); // PBXGroup
}
public void addFramework(PBXFile file) throws PBXProjectException {
file.setFramework(true);
addToPbxBuildFileSection(file); // PBXBuildFile
addToPbxFileReferenceSection(file); // PBXFileReference
addToPbxGroup("Frameworks", file); // PBXGroup
addToBuildPhase("PBXFrameworksBuildPhase",file); // PBXFrameworksBuildPhase
}
public void addHeaderFile(PBXFile file) throws PBXProjectException {
this.addPluginFile(file);
}
public void addResourceFile(PBXFile file) throws PBXProjectException {
if (file.isPlugin()) {
this.addPluginFile(file);
}
if (!file.isPlugin()) {
NSDictionary resGroup = getGroupByName("Resources");
if(resGroup.containsKey("path")){
String path = file.getPath();
int index = path.indexOf("Resources/");
if(index > -1){
file.setPath(path.substring(index+"Resources/".length()));
}
}
}
addToPbxBuildFileSection(file); // PBXBuildFile
addToBuildPhase("PBXResourcesBuildPhase",file); // PBXResourcesBuildPhase
if(!file.isPlugin()){
addToPbxFileReferenceSection(file); // PBXFileReference
addToPbxGroup("Resources",file); // PBXGroup
}
}
/**
* Adds the pbxfile to library search paths. The path of the
* pbxfile must be project relative.
*
* @param pbxfile
* @throws PBXProjectException
*/
public void addToLibrarySearchPaths(PBXFile pbxfile) throws PBXProjectException {
HashMap<String, NSObject> hashmap = getObjects().getHashMap();
Collection<NSObject> values = hashmap.values();
for (NSObject nsObject : values) {
NSDictionary obj = (NSDictionary) nsObject;
NSString isa = (NSString) obj.objectForKey("isa");
if(isa != null && isa.getContent().equals("XCBuildConfiguration")){
NSDictionary buildSettings = (NSDictionary) obj.objectForKey("buildSettings");
NSArray arr = null;
if( buildSettings.containsKey(KEY_LIBRARY_SEARCH_PATHS)){
arr = (NSArray) buildSettings.objectForKey(KEY_LIBRARY_SEARCH_PATHS);
}
if(arr == null){//new search path entry
arr = new NSArray(NSObject.wrap("$(inherited)"), searchPathForFile(pbxfile));
}else{//modify existing one
Object[] current = arr.getArray();
NSObject[] newArray = new NSObject[current.length + 1];
System.arraycopy(current, 0, newArray, 0, current.length);
newArray[newArray.length -1 ] = searchPathForFile(pbxfile);
}
buildSettings.put(KEY_LIBRARY_SEARCH_PATHS, arr);
}
}
}
private NSString searchPathForFile(PBXFile pbxfile) throws PBXProjectException {
String filepath = FilenameUtils.getFullPathNoEndSeparator(pbxfile.getPath());
if(filepath.equals(".")){
filepath = "";
}else{
filepath = "/"+filepath;
}
NSDictionary group = getGroupByName("Plugins");
if(pbxfile.isPlugin() && group.containsKey("path")){
NSString groupPath = (NSString)group.objectForKey("path");
return NSObject.wrap("$(SRCROOT)/" + groupPath.getContent().replace('"', ' ').trim());
}
else{
return NSObject.wrap("$(SRCROOT)/"+ getProductName() + filepath );
}
}
public String getProductName() throws PBXProjectException {
HashMap<String, NSObject> hashmap = getObjects().getHashMap();
Collection<NSObject> values = hashmap.values();
for (NSObject nsObject : values) {
NSDictionary obj = (NSDictionary) nsObject;
NSString isa = (NSString) obj.objectForKey("isa");
if(isa != null && isa.getContent().equals("XCBuildConfiguration")){
NSDictionary buildSettings = (NSDictionary) obj.objectForKey("buildSettings");
if( buildSettings.containsKey("PRODUCT_NAME")){
NSString name = (NSString) buildSettings.get("PRODUCT_NAME");
return name.getContent().replace('"', ' ').trim();
}
}
}
return null;
}
public String getContent() throws PBXProjectException{
return getRoot().toASCIIPropertyList();
}
public void persist() throws IOException, PBXProjectException{
PropertyListParser.saveAsASCII(getRoot(), this.file);
}
private void addToBuildPhase(String phaseName, PBXFile pbxfile) throws PBXProjectException {
NSDictionary phase = getPhaseByName(phaseName);
NSArray files = (NSArray) phase.objectForKey("files");
NSObject[] current = files.getArray();
NSObject[] newArray = new NSObject[ current.length +1 ];
System.arraycopy(current, 0, newArray, 0, current.length);
newArray[newArray.length-1] = new NSString(pbxfile.getUuid());
NSArray newNSArray = new NSArray(newArray);
phase.remove("files");
phase.put("files", newNSArray);
}
private void addToPbxBuildFileSection(PBXFile pbxfile) throws PBXProjectException {
NSDictionary obj = new NSDictionary();
obj.put("isa" , "PBXBuildFile");
obj.put("fileRef", pbxfile.getFileRef());
if (pbxfile.hasSettings()){
NSDictionary settings = new NSDictionary();
if(pbxfile.isWeak()){
NSArray attribs = new NSArray(NSObject.wrap("Weak"));
settings.put("ATTRIBUTES", attribs);
}
if(pbxfile.getCompilerFlags() != null ){
settings.put("COMPILER_FLAGS", NSObject.wrap(pbxfile.getCompilerFlags()));
}
obj.put("settings", settings);
}
getObjects().put(pbxfile.getUuid(), obj);
}
private void addToPbxGroup(String groupName, PBXFile pbxfile) throws PBXProjectException {
NSDictionary group = getGroupByName(groupName);
NSArray children = (NSArray) group.objectForKey("children");
NSObject[] childs = children.getArray();
NSObject[] newChilds = new NSObject[childs.length +1];
System.arraycopy(childs, 0, newChilds, 0, childs.length);
newChilds[newChilds.length-1] = new NSString(pbxfile.getFileRef());
NSArray newArray = new NSArray(newChilds);
group.remove("children");
group.put("children", newArray);
}
private void addToPbxFileReferenceSection(PBXFile pbxfile) throws PBXProjectException {
NSDictionary obj = new NSDictionary();
obj.put("isa", "PBXFileReference");
obj.put("lastKnownFileType", pbxfile.getLastType());
obj.put("path", pbxfile.getPath());
obj.put("name", FilenameUtils.getName(pbxfile.getPath()));
obj.put("sourceTree", pbxfile.getSourceTree());
if(pbxfile.getEncoding() != null){
obj.put("fileEncoding", pbxfile.getEncoding());
}
getObjects().put(pbxfile.getFileRef(), obj);
}
private NSDictionary getRoot() throws PBXProjectException{
if(this.root == null ){
try {
root = (NSDictionary) ASCIIPropertyListParser.parse(file);
} catch (Exception e) {
throw new PBXProjectException(e);
}
}
return root;
}
private NSDictionary getObjects() throws PBXProjectException {
NSDictionary dict = (NSDictionary) getRoot();
NSDictionary objects = (NSDictionary)dict.getHashMap().get("objects");
return objects;
}
private NSDictionary getPhaseByName(String name) throws PBXProjectException{
NSDictionary objects = getObjects();
HashMap<String, NSObject> map = objects.getHashMap();
Collection<NSObject> values = map.values();
for (NSObject nsObject : values) {
NSDictionary obj = (NSDictionary)nsObject;
NSString isa = (NSString) obj.objectForKey("isa");
if(isa != null && isa.getContent().equals(name)){
return obj;
}
}
return null;
}
private NSDictionary getGroupByName(String name) throws PBXProjectException{
NSDictionary objects = getObjects();
HashMap<String, NSObject> map = objects.getHashMap();
Collection<NSObject> values = map.values();
for (NSObject nsObject : values) {
NSDictionary obj = (NSDictionary)nsObject;
NSString isa = (NSString) obj.objectForKey("isa");
NSString nameString = (NSString) obj.objectForKey("name");
if(isa != null && isa.getContent().equals("PBXGroup") && nameString != null && name.equals(nameString.getContent())){
return obj;
}
}
return null;
}
public static String generateReference()
{
try{
MessageDigest md = MessageDigest.getInstance("SHA1");
SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");
String randomNum = Integer.toString(prng.nextInt());
String ref = new String(Hex.encodeHex(md.digest(randomNum.getBytes())));
return ref.toUpperCase().substring(0, 24);
}
catch (NoSuchAlgorithmException e){
return null;
}
}
}