content: /*
   * Copyright (C) 2011 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.taobao.android.dx.command.grep;
  
  import com.taobao.android.dex.ClassData;
  import com.taobao.android.dex.ClassDef;
  import com.taobao.android.dex.Dex;
  import com.taobao.android.dex.EncodedValueReader;
  import com.taobao.android.dex.MethodId;
  import com.taobao.android.dx.io.CodeReader;
  import com.taobao.android.dx.io.instructions.DecodedInstruction;
  import java.io.PrintWriter;
  import java.util.HashSet;
  import java.util.Set;
  import java.util.regex.Pattern;
  
  public final class Grep {
      private final Dex dex;
      private final CodeReader codeReader = new CodeReader();
      private final Set<Integer> stringIds;
  
      private final PrintWriter out;
      private int count = 0;
  
      private ClassDef currentClass;
      private ClassData.Method currentMethod;
  
      public Grep(final Dex dex, Pattern pattern, final PrintWriter out) {
          this.dex = dex;
          this.out = out;
  
          stringIds = getStringIds(dex, pattern);
  
          codeReader.setStringVisitor(new CodeReader.Visitor() {
              public void visit(DecodedInstruction[] all, DecodedInstruction one) {
                  encounterString(one.getIndex());
              }
          });
      }
  
      private void readArray(EncodedValueReader reader) {
          for (int i = 0, size = reader.readArray(); i < size; i++) {
              switch (reader.peek()) {
              case EncodedValueReader.ENCODED_STRING:
                  encounterString(reader.readString());
                  break;
              case EncodedValueReader.ENCODED_ARRAY:
                  readArray(reader);
                  break;
              }
          }
      }
  
      private void encounterString(int index) {
          if (stringIds.contains(index)) {
              out.println(location() + " " + dex.strings().get(index));
              count++;
          }
      }
  
      private String location() {
          String className = dex.typeNames().get(currentClass.getTypeIndex());
          if (currentMethod != null) {
              MethodId methodId = dex.methodIds().get(currentMethod.getMethodIndex());
              return className + "." + dex.strings().get(methodId.getNameIndex());
          } else {
              return className;
          }
      }
  
      /**
       * Prints usages to out. Returns the number of matches found.
       */
      public int grep() {
          for (ClassDef classDef : dex.classDefs()) {
              currentClass = classDef;
              currentMethod = null;
  
              if (classDef.getClassDataOffset() == 0) {
                  continue;
              }
  
              ClassData classData = dex.readClassData(classDef);
  
              // find the strings in encoded constants
              int staticValuesOffset = classDef.getStaticValuesOffset();
              if (staticValuesOffset != 0) {
                  readArray(new EncodedValueReader(dex.open(staticValuesOffset)));
              }
  
              // find the strings in method bodies
              for (ClassData.Method method : classData.allMethods()) {
                  currentMethod = method;
                  if (method.getCodeOffset() != 0) {
                      codeReader.visitAll(dex.readCode(method).getInstructions());
                  }
              }
          }
  
          currentClass = null;
          currentMethod = null;
          return count;
      }
  
      private Set<Integer> getStringIds(Dex dex, Pattern pattern) {
          Set<Integer> stringIds = new HashSet<Integer>();
          int stringIndex = 0;
          for (String s : dex.strings()) {
              if (pattern.matcher(s).find()) {
                  stringIds.add(stringIndex);
              }
              stringIndex++;
          }
          return stringIds;
      }
  }
  