001/**
002 * Copyright 2013 John Ericksen
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.androidtransfuse.util;
017
018import org.androidtransfuse.TransfuseAnalysisException;
019
020import javax.annotation.processing.Filer;
021import javax.inject.Inject;
022import javax.inject.Named;
023import javax.tools.JavaFileObject;
024import java.io.File;
025import java.io.IOException;
026import java.net.URI;
027import java.net.URISyntaxException;
028
029/**
030 * Copied respectfully from the AndroidAnnotations project:
031 * com.googlecode.androidannotations.helper.AndroidManifestFinder
032 *
033 * Original Header:
034 *
035 * Copyright (C) 2010-2011 eBusiness Information, Excilys Group
036 *
037 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
038 * use this file except in compliance with the License. You may obtain a copy of
039 * the License at
040 *
041 * http://www.apache.org/licenses/LICENSE-2.0
042 *
043 * Unless required by applicable law or agreed To in writing, software
044 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
045 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
046 * License for the specific language governing permissions and limitations under
047 * the License.
048 *
049 * @author John Ericksen
050 */
051public class ManifestLocator {
052        public static final String ANDROID_MANIFEST_FILE_OPTION = "androidManifestFile";
053
054    private static final int MAX_PARENTS_FROM_SOURCE_FOLDER = 10;
055
056    private final String androidManifestFilePath;
057    private final Filer filer;
058    private final Logger logger;
059
060    @Inject
061    public ManifestLocator(Filer filer, Logger logger, @Named(ANDROID_MANIFEST_FILE_OPTION) String androidManifestFilePath) {
062        this.filer = filer;
063        this.logger = logger;
064        this.androidManifestFilePath = androidManifestFilePath;
065    }
066
067    public File findManifest() {
068        File androidManifestFile;
069
070        try {
071            if (androidManifestFilePath != null) {
072                androidManifestFile = new File(androidManifestFilePath);
073
074                if (!androidManifestFile.exists()) {
075                    throw new IllegalStateException("Could not find the AndroidManifest.xml file specified by option " + ANDROID_MANIFEST_FILE_OPTION + " [" + androidManifestFilePath + "]");
076                } else {
077                    //todo: debug logging
078                    // logger.info("AndroidManifest.xml file found: " + androidManifestFile.toString());
079                }
080            } else {
081                // Backwards compatibility
082                androidManifestFile = findManifestFileThrowing();
083            }
084        } catch (URISyntaxException e) {
085            logger.error("URISyntaxException while trying to load manifest", e);
086            throw new TransfuseAnalysisException("URISyntaxException while trying to load manifest", e);
087        } catch (IOException e) {
088            logger.error("IOException while trying to load manifest", e);
089            throw new TransfuseAnalysisException("IOException while trying to load manifest", e);
090        }
091
092        return androidManifestFile;
093    }
094
095
096    /**
097     * We use a dirty trick to find the AndroidManifest.xml file, since it's not
098     * available in the classpath. The idea is quite simple : create a fake
099     * class file, retrieve its URI, and start going up in parent folders to
100     * find the AndroidManifest.xml file. Any better solution will be
101     * appreciated.
102     */
103    private File findManifestFileThrowing() throws IOException, URISyntaxException {
104        JavaFileObject dummySourceFile = filer.createSourceFile("dummy" + System.currentTimeMillis());
105        String dummySourceFilePath = dummySourceFile.toUri().toString();
106
107        if (dummySourceFilePath.startsWith("file:")) {
108            if (!dummySourceFilePath.startsWith("file://")) {
109                dummySourceFilePath = "file://" + dummySourceFilePath.substring("file:".length());
110            }
111        } else {
112            dummySourceFilePath = "file://" + dummySourceFilePath;
113        }
114
115        //todo: debug logging
116        // logger.info("Dummy source file: " + dummySourceFilePath);
117
118        URI cleanURI = new URI(dummySourceFilePath);
119
120        File dummyFile = new File(cleanURI);
121
122        File sourcesGenerationFolder = dummyFile.getParentFile();
123
124        File projectRoot = sourcesGenerationFolder.getParentFile();
125
126        File androidManifestFile = new File (projectRoot, "AndroidManifest.xml");
127        for (int i = 0; i < MAX_PARENTS_FROM_SOURCE_FOLDER; i++) {
128            if (androidManifestFile.exists()) {
129                break;
130            } else {
131                if (projectRoot.getParentFile() != null) {
132                    projectRoot = projectRoot.getParentFile();
133                    androidManifestFile = new File(projectRoot, "AndroidManifest.xml");
134                } else {
135                    break;
136                }
137            }
138        }
139
140        if (!androidManifestFile.exists()) {
141            throw new IllegalStateException("Could not find the AndroidManifest.xml file, going up from path [" + sourcesGenerationFolder.getAbsolutePath() + "] found using dummy file [" + dummySourceFilePath + "] (max atempts: " + MAX_PARENTS_FROM_SOURCE_FOLDER + ")");
142        } else {
143            //todo: debug logging
144            // logger.info("AndroidManifest.xml file found: " + androidManifestFile.toString());
145        }
146
147        return androidManifestFile;
148    }
149
150}