blob: 7e3095ff4ead85fb4c014cb0e45bf02f3a433f6a [file] [log] [blame]
/*
* 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;
}
}