package jvm; import jvm.classfile.ClassFile; import jvm.classfile.ClassParser; import jvm.exception.ClassDuplicateException; import jvm.exception.ClassNotExistsException; import jvm.exception.ReadClassException; import jvm.util.ArrayUtils; import jvm.util.ByteUtils; import java.io.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class ClassFileLoader { private List<String> classPaths = new ArrayList<>(); public byte[] readBinaryCode(String className) throws ReadClassException { File file = getClassFile(className); if (file == null) { throw new ClassNotExistsException(); } InputStream is; try { is = new FileInputStream(file); } catch (FileNotFoundException e) { throw new ClassNotExistsException(); } List<Byte> bytes = new ArrayList<>(); byte[] buf = new byte[1024]; int len; try { while ((len = is.read(buf)) != -1) { bytes.addAll(ArrayUtils.toList(buf, 0, len)); } } catch (IOException e) { e.printStackTrace(); } return ArrayUtils.toArray(bytes); } private File getClassFile(String className) throws ClassDuplicateException { int split = className.lastIndexOf('.'); if (split == -1) { split = className.lastIndexOf('/'); } String fileName = className.substring(split + 1) + ".class"; List<File> files = new ArrayList<>(); for (String path : classPaths) { files.addAll(getFiles(new File(path), fileName)); } if (files.size() > 1) { throw new ClassDuplicateException(); } return files.size() == 1 ? files.get(0) : null; } private List<File> getFiles(File path, String fileName) { List<File> files = new ArrayList<>(); File[] listFile = path.listFiles(); if (listFile == null) { return files; } for (File f : listFile) { if (f.isDirectory()) { files.addAll(getFiles(f, fileName)); } else if (f.getName().equals(fileName)) { files.add(f); } } return files; } public void addClassPath(String path) { if (path != null && !"".equals(path)) { classPaths.add(path); } } public String getClassPath() { StringBuilder builder = new StringBuilder(); classPaths.forEach((i) -> builder.append(';').append(i)); return builder.substring(1); } boolean checkMagicNumber(byte[] bytes) { String magicNumber = "cafebabe"; String str = ByteUtils.toHexString(bytes, 0, 4); return magicNumber.equals(str.toLowerCase()); } public ClassFile load(String className) throws ReadClassException { byte[] bytes = readBinaryCode(className); if (checkMagicNumber(bytes)) { return ClassParser.parse(bytes); } return null; } }