/* * Copyright 2014-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.apple; import com.dd.plist.BinaryPropertyListWriter; import com.dd.plist.NSDictionary; import com.dd.plist.NSObject; import com.dd.plist.PropertyListParser; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.step.ExecutionContext; import com.facebook.buck.step.Step; import com.facebook.buck.step.StepExecutionResult; import com.google.common.collect.ImmutableMap; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; import java.util.Optional; class PlistProcessStep implements Step { /** Controls what format the plist is output in. */ public enum OutputFormat { /** Output the XML plist format. */ XML, /** Output the Apple binary plist format. */ BINARY, ; } private final ProjectFilesystem filesystem; private final Path input; private final Optional<Path> additionalInputToMerge; private final Path output; /** Only valid if the input .plist is a NSDictionary; ignored otherwise. */ private final ImmutableMap<String, NSObject> additionalKeys; /** Only valid if the input .plist is a NSDictionary; ignored otherwise. */ private final ImmutableMap<String, NSObject> overrideKeys; private final OutputFormat outputFormat; public PlistProcessStep( ProjectFilesystem filesystem, Path input, Optional<Path> additionalInputToMerge, Path output, ImmutableMap<String, NSObject> additionalKeys, ImmutableMap<String, NSObject> overrideKeys, OutputFormat outputFormat) { this.filesystem = filesystem; this.input = input; this.additionalInputToMerge = additionalInputToMerge; this.output = output; this.additionalKeys = additionalKeys; this.overrideKeys = overrideKeys; this.outputFormat = outputFormat; } @Override public StepExecutionResult execute(ExecutionContext context) throws InterruptedException { try (InputStream stream = filesystem.newFileInputStream(input); BufferedInputStream bufferedStream = new BufferedInputStream(stream)) { NSObject infoPlist; try { infoPlist = PropertyListParser.parse(bufferedStream); } catch (Exception e) { throw new IOException(input.toString() + ": " + e); } if (infoPlist instanceof NSDictionary) { NSDictionary dictionary = (NSDictionary) infoPlist; if (additionalInputToMerge.isPresent()) { try (InputStream mergeStream = filesystem.newFileInputStream(additionalInputToMerge.get()); BufferedInputStream mergeBufferedStream = new BufferedInputStream(mergeStream)) { NSObject mergeInfoPlist; try { mergeInfoPlist = PropertyListParser.parse(mergeBufferedStream); } catch (Exception e) { throw new IOException(additionalInputToMerge.toString() + ": " + e); } dictionary.putAll(((NSDictionary) mergeInfoPlist).getHashMap()); } } for (ImmutableMap.Entry<String, NSObject> entry : additionalKeys.entrySet()) { if (!dictionary.containsKey(entry.getKey())) { dictionary.put(entry.getKey(), entry.getValue()); } } dictionary.putAll(overrideKeys); } switch (this.outputFormat) { case XML: String serializedInfoPlist = infoPlist.toXMLPropertyList(); filesystem.writeContentsToPath(serializedInfoPlist, output); break; case BINARY: byte[] binaryInfoPlist = BinaryPropertyListWriter.writeToArray(infoPlist); filesystem.writeBytesToPath(binaryInfoPlist, output); break; } } catch (IOException e) { context.logError(e, "error parsing plist %s", input); return StepExecutionResult.ERROR; } return StepExecutionResult.SUCCESS; } @Override public String getShortName() { return "process-plist"; } @Override public String getDescription(ExecutionContext context) { return String.format("process-plist %s %s", input, output); } }