/* * Copyright (C) 2012 The Android Open Source Project * * 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.motorolamobility.preflighting.core.applicationdata; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import org.eclipse.jdt.core.dom.CompilationUnit; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.motorolamobility.preflighting.core.applicationdata.Element.Type; import com.motorolamobility.preflighting.core.exception.PreflightingToolException; import com.motorolamobility.preflighting.core.internal.utils.ProjectUtils; import com.motorolamobility.preflighting.core.internal.utils.StringUsageIdentifier; import com.motorolamobility.preflighting.core.source.model.SourceFileElement; import com.motorolamobility.preflighting.core.validation.Parameter; /** * This Class represents the Application either in project or APK format that * will be analyzed by the tool. * * It has a root node attribute that points to the root Element of the * Application. From that node is possible to navigate through the Application * tree. * */ public class ApplicationData { private Element rootElement; private String rootElementPath; private int appVer; private String appName; private String applicationPath; private XMLElement manifestElement; private List<XMLElement> layoutElements = new java.util.ArrayList<XMLElement>(); private List<XMLElement> stringElements = new java.util.ArrayList<XMLElement>(); private List<XMLElement> xmlElements = new java.util.ArrayList<XMLElement>(); private boolean isProject = false; private List<SourceFolderElement> sourceFolderElements = new java.util.ArrayList<SourceFolderElement>(); private List<Certificate> certificateChain; /** * Construct a new ApplicationData with the given parameters. * * @param globalParams the list of global parameters. * @throws PreflightingToolException * In case the applcationPath does not point to a APK or valid * Android Project. */ public ApplicationData(List<Parameter> globalParams) throws PreflightingToolException { ProjectUtils.populateAplicationData(globalParams, this); initialize(); } /*** * Initialize the class properties by reading the Application tree * */ public void initialize() { // Manifest Element List<Element> manifestList = ElementUtils.getElementByType(rootElement, Element.Type.FILE_MANIFEST); if (manifestList.size() > 0) { if (manifestList.get(0) instanceof XMLElement) { manifestElement = (XMLElement) manifestList.get(0); NodeList manifestByTag = manifestElement.getDocument().getElementsByTagName("manifest"); Node manifest = (manifestByTag != null) && (manifestByTag.getLength() > 0) ? manifestByTag .item(0) : null; Node versionCode = null; if (manifest != null) { versionCode = manifest.getAttributes().getNamedItem("android:versionCode"); } try { appVer = Integer.parseInt(versionCode.getTextContent()); } catch (Exception e) { appVer = 0; } } } // Layout Elements List<Element> layoutsList = ElementUtils.getElementByType(rootElement, Element.Type.FILE_LAYOUT); if (layoutsList != null) { for (Element element : layoutsList) { if (element instanceof XMLElement) { layoutElements.add((XMLElement) element); } } } // String Elements List<Element> stringsList = ElementUtils.getElementByType(rootElement, Element.Type.FILE_STRINGS); if (stringsList != null) { for (Element element : stringsList) { if (element instanceof XMLElement) { stringElements.add((XMLElement) element); } } } //Source Element List<Element> srcFolders = ElementUtils.getElementByType(rootElement, Element.Type.FOLDER_SRC); for (Element srcFolder : srcFolders) { if (srcFolder instanceof SourceFolderElement) { sourceFolderElements.add((SourceFolderElement) srcFolder); } } xmlElements = ElementUtils.getXMLElements(rootElement); } /** * Return the Application root element. * @return The Element which represents the Application root element. */ public Element getRootElement() { return rootElement; } /** * Set the ApplicationData to a given Element. * @param rootElement The Element. */ public void setRootElement(Element rootElement) { this.rootElement = rootElement; } /** * Return the Application root element path. * @return A string with the Application root element path. */ public String getRootElementPath() { return rootElementPath; } /** * Set the Application root element path. * @param rootElementPath The element path. */ public void setRootElementPath(String rootElementPath) { this.rootElementPath = rootElementPath; } /** * Return the Element which corresponds to the Manifest file. * @return A XML Element of the Manifest file. */ public XMLElement getManifestElement() { return manifestElement; } /** * Return the Element list which corresponds to the Layout files. * @return A list of Elements of the layout files. */ public List<XMLElement> getLayoutElements() { return layoutElements; } /** * Return the Element list which corresponds to the Localization String files. * @return A list of Elements of the Localization String files. */ public List<XMLElement> getStringElements() { return stringElements; } public List<XMLElement> getXMLElements() { return xmlElements; } /** * Return wheter the ApplicationData belongs to a project. * @return A Boolean stating whether it is a project or not. */ public boolean isProject() { return isProject; } /** * Set whether the ApplicationData belongs to a project or not. * @param isProject A Boolean stating whether it is a project or not. */ public void setIsProject(boolean isProject) { this.isProject = isProject; } /** * Return the list of certificates. * @return A list of certificates, empty if the application (project or APK) does not have any certificates. */ public List<Certificate> getCertificateChain() { return certificateChain; } /** * Sets the Certificate Chain of the Application. * @param certificateChain a list of certificates. */ public void setCertificateChain(List<Certificate> certificateChain) { this.certificateChain = certificateChain; } /** * Cleans all the data from the ApplicationData. */ public void clean() { if (this.rootElement != null) { this.rootElement.clean(); } this.rootElement = null; if (this.manifestElement != null) { this.manifestElement.clean(); } this.manifestElement = null; if (this.layoutElements != null) { for (XMLElement elem : this.layoutElements) { elem.clean(); } this.layoutElements.clear(); } this.layoutElements = null; if (this.stringElements != null) { for (XMLElement elem : this.stringElements) { elem.clean(); } this.stringElements.clear(); } this.stringElements = null; if (this.certificateChain != null) { this.certificateChain.clear(); } this.certificateChain = null; if (this.sourceFolderElements != null) { this.sourceFolderElements.clear(); } } /** * Return the Application Version. * @return An Integer representing the application version. */ public int getVersion() { return appVer; } /** * Set the Application name. * @param appName A string with the application name. */ public void setName(String appName) { this.appName = appName; } /** * Return the Application name. * @return A string with the application name. */ public String getName() { return appName; } /** * Set the Application path. * @param applicationPath A string with the application path. */ public void setApplicationPath(String applicationPath) { this.applicationPath = applicationPath; } /** * Return the Application path. * @return A string with the application path. */ public String getApplicationPath() { return applicationPath; } /** * Return a {@link SourceFolderElement} list which corresponds to the Java Model. * @return A {@link SourceFolderElement} list which corresponds to the Java Model. */ public List<SourceFolderElement> getJavaModel() { return sourceFolderElements; } /** * * Set a {@link SourceFolderElement} list which corresponds to the Java Model. * @param javaModel A {@link SourceFolderElement} list which corresponds to the Java Model. */ public void setJavaModel(List<SourceFolderElement> javaModel) { this.sourceFolderElements = javaModel; } /** * Returns project list of {@link CompilationUnit} to enable checkers/conditions that are project specific (not available for APK verifications). * @return The list of {@link CompilationUnit} objects. */ public List<CompilationUnit> getProjectCompilationUnits() { List<CompilationUnit> projectCompilationUnits = new ArrayList<CompilationUnit>(); if (this.sourceFolderElements != null) { for (SourceFolderElement folder : this.sourceFolderElements) { for (SourceFileElement file : folder.getSourceFileElements()) { projectCompilationUnits.add(file.getCompilationUnit()); } } } return projectCompilationUnits; } /** * Gets the set of strings used by the app (identified by string ID, not including "R.string") * @return set with the used string in the XML files */ private Set<String> getUsedStringsInXMLFiles() { return StringUsageIdentifier.identifyStringsUsed(xmlElements); } /** * Gets the set of strings used by the app (identified by string ID, not including "R.string") * @return set with the used strings in the Java files */ private Set<String> getUsedStringsInJavaFiles() { Set<String> usedStringsInJava = new HashSet<String>(); if (this.sourceFolderElements != null) { for (SourceFolderElement folder : this.sourceFolderElements) { usedStringsInJava.addAll(folder.getUsedStringConstants()); } } return usedStringsInJava; } /** * Gets the set of used strings (they contain only the id of the string, that is, it does not include R.string) * @return the complete set of strings used throughout the application (either in Java or XML files) */ public Set<String> getUsedStringsInApplication() { Set<String> usedStringsInApplication = new HashSet<String>(); usedStringsInApplication.addAll(getUsedStringsInXMLFiles()); usedStringsInApplication.addAll(getUsedStringsInJavaFiles()); return usedStringsInApplication; } /** * Gets the set of declared strings (based on default locale string keys merged with other locale string keys) * WARNING: Before calling this method, use {@link Condition#canExecute(ApplicationData, List)} to verify if there is a res folder in the application * @return the set of strings declared in the app's resource XML files */ public Set<String> getDeclaredStringsInResourceFiles() { Set<String> declaredStrings = new HashSet<String>(); List<Element> folderResElements = ElementUtils.getElementByType(this.getRootElement(), Type.FOLDER_RES); ResourcesFolderElement resFolder = folderResElements.size() > 0 ? (ResourcesFolderElement) folderResElements.get(0) : null; if (resFolder != null) { StringsElement stringsKeysDefault = resFolder.getDefaultValuesElement(); if ((stringsKeysDefault != null) && (stringsKeysDefault.getKeyList() != null)) { //adding keys from default locale declaredStrings.addAll(stringsKeysDefault.getKeyList()); } for (Locale l : resFolder.getAvailableLocales()) { StringsElement stringsKeysLocale = resFolder.getValuesElement(l); if ((stringsKeysLocale != null) && (stringsKeysLocale.getKeyList() != null)) { //adding keys from non-default locales declaredStrings.addAll(stringsKeysLocale.getKeyList()); } } } return declaredStrings; } }