/* SAAF: A static analyzer for APK files.
* Copyright (C) 2013 syssec.rub.de
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.rub.syssec.saaf.application;
import java.util.Arrays;
import de.rub.syssec.saaf.application.instructions.Instruction;
import de.rub.syssec.saaf.application.manifest.permissions.Permission;
import de.rub.syssec.saaf.misc.ByteUtils;
import de.rub.syssec.saaf.model.application.ClassInterface;
import de.rub.syssec.saaf.model.application.CodeLineInterface;
import de.rub.syssec.saaf.model.application.MethodInterface;
import de.rub.syssec.saaf.model.application.instruction.InstructionInterface;
import de.rub.syssec.saaf.model.application.instruction.InstructionType;
/**
* This class represents one line of code. The codeline will be stripped of all leading
* and trailing whitespace and unprintable bytes in the range of 0 to 32.
*/
public class CodeLine implements CodeLineInterface {
private final byte[] line;
private final int lineNr;
private final InstructionInterface instruction;
private final ClassInterface sf;
private Permission permission = null;
/**
* A reference to the method where this cl comes from, may be null!
*/
private MethodInterface method = null;
/**
* This class represent one line from a SmaliClass.
* @param line the bytes from the line from the original SmaliClass
* @param lineNr the line number in the file
* @param sf a reference to the SmaliClass
*/
protected CodeLine(byte[] line, int lineNr, ClassInterface sf) {
this.line = trim(line);
this.lineNr = lineNr;
this.sf = sf;
instruction = new Instruction(this);
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#getLine()
*/
@Override
public byte[] getLine() {
return line;
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#getLineNr()
*/
@Override
public int getLineNr() {
return lineNr;
}
StringBuilder clSb;
@Override
public String toString() {
if (clSb == null) { // init
clSb = new StringBuilder();
clSb.append(lineNr);
clSb.append(": ");
clSb.append(new String(line));
}
return clSb.toString();
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#getNrAndLine()
*/
@Override
public String getNrAndLine(){
return lineNr+" "+new String(line);
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#isEmpty()
*/
@Override
public boolean isEmpty() {
if (line.length == 0) return true;
else return false;
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#isCode()
*/
@Override
public boolean isCode() {
if ( instruction.getType() == InstructionType.NOT_YET_PARSED || instruction.getType() == InstructionType.SMALI_DOT_COMMENT
|| instruction.getType() == InstructionType.EMPTY_LINE || instruction.getType() == InstructionType.LABEL
|| instruction.getType() == InstructionType.SMALI_HASH_KEY_COMMENT
|| instruction.getType() == InstructionType.UNKNOWN
|| instruction.getType() == InstructionType.NOP) return false;
else return true;
}
/**
* Deletes all whitespace and non printable bytes (bytes <= 32) from the beginning and the end of the byte array
* @param line
* @return
*/
private static byte[] trim(byte[] line) {
int begin = 0;
while (begin < line.length && line[begin] <= 32) { begin++; }
int end = line.length-1;
while (end >= 0 && line[end] <= 32) { end--; }
if (end < begin) return new byte[0];
else return Arrays.copyOfRange(line, begin, end+1);
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#startsWith(byte[])
*/
@Override
public boolean startsWith(byte[] pattern) {
return ByteUtils.startsWith(line, pattern);
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#startsWith(java.lang.String)
*/
@Override
@Deprecated
public boolean startsWith(String pattern) {
return ByteUtils.startsWith(line, pattern.getBytes());
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#contains(byte[])
*/
@Override
public boolean contains(byte[] pattern) {
return ByteUtils.contains(line, pattern);
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#contains(java.lang.String)
*/
@Override
@Deprecated
public boolean contains(String pattern) {
return ByteUtils.contains(line, pattern.getBytes());
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#getInstruction()
*/
@Override
public InstructionInterface getInstruction() {
return instruction;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(line);
result = prime * result + lineNr;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CodeLineInterface other = (CodeLineInterface) obj;
if (!Arrays.equals(line, other.getLine()))
return false;
if (lineNr != other.getLineNr())
return false;
return true;
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#getSmaliClass()
*/
@Override
public ClassInterface getSmaliClass() {
return sf;
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#getMethod()
*/
@Override
public MethodInterface getMethod() {
return method;
}
/* (non-Javadoc)
* @see de.rub.syssec.saaf.application.CodeLineInterface#setMethod(de.rub.syssec.saaf.application.Method)
*/
@Override
public void setMethod(MethodInterface method) {
this.method = method;
}
public void setPermission(Permission perm){
this.permission = perm;
}
public Permission getPermission(){
return permission;
}
}