/******************************************************************************* * 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; } } }