/*- * Copyright (C) 2009 Erik Larsson * * 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 org.catacombae.hfs; import java.util.LinkedList; import org.catacombae.hfs.io.ForkFilter; import org.catacombae.hfs.types.hfscommon.CommonBTNode; import org.catacombae.hfs.types.hfscommon.CommonHFSCatalogNodeID.ReservedID; import org.catacombae.hfs.types.hfscommon.CommonHFSVolumeHeader; import org.catacombae.io.ReadableRandomAccessStream; import org.catacombae.hfs.plus.HFSPlusVolume; import org.catacombae.hfs.types.hfscommon.CommonBTHeaderNode; import org.catacombae.hfs.types.hfscommon.CommonBTIndexRecord; import org.catacombae.hfs.types.hfscommon.CommonHFSAttributesIndexNode; import org.catacombae.hfs.types.hfscommon.CommonHFSAttributesKey; import org.catacombae.hfs.types.hfscommon.CommonHFSAttributesLeafNode; import org.catacombae.hfs.types.hfscommon.CommonHFSAttributesLeafRecord; import org.catacombae.hfs.types.hfscommon.CommonHFSCatalogNodeID; import org.catacombae.hfs.types.hfsplus.HFSPlusAttributesKey; /** * @author <a href="http://www.catacombae.org/" target="_top">Erik Larsson</a> */ public class AttributesFile extends BTreeFile<CommonHFSAttributesKey, CommonHFSAttributesLeafRecord> { private final HFSPlusVolume view; private class Session extends BTreeFileSession { ReadableRandomAccessStream attributesFileStream; protected ReadableRandomAccessStream getBTreeStream( CommonHFSVolumeHeader header) { if(!(header instanceof CommonHFSVolumeHeader.HFSPlusImplementation)) { throw new RuntimeException("Illegal CommonHFSVolumeHeader " + "flavour (expected HFSPlusImplementation, got " + header.getClass() + ")."); } if(this.attributesFileStream == null) { this.attributesFileStream = getAttributesFileStream( (CommonHFSVolumeHeader.HFSPlusImplementation) header); } return this.attributesFileStream; } } public AttributesFile(HFSPlusVolume view) { super(view); this.view = view; } private ReadableRandomAccessStream getAttributesFileStream( CommonHFSVolumeHeader.HFSPlusImplementation header) { return new ForkFilter(ForkFilter.ForkType.DATA, vol.getCommonHFSCatalogNodeID(ReservedID.ATTRIBUTES_FILE). toLong(), header.getAttributesFile(), vol.extentsOverflowFile, view.createFSStream(), 0, header.getAllocationBlockSize(), header.getAllocationBlockStart() * view.getPhysicalBlockSize()); } protected BTreeFileSession openSession() { return new Session(); } protected CommonHFSAttributesIndexNode createIndexNode(byte[] nodeData, int offset, int nodeSize) { return CommonHFSAttributesIndexNode.createHFSPlus(nodeData, 0, nodeSize); } protected CommonHFSAttributesLeafNode createLeafNode(byte[] nodeData, int offset, int nodeSize) { return CommonHFSAttributesLeafNode.createHFSPlus(nodeData, 0, nodeSize); } public CommonBTHeaderNode getHeaderNode() { CommonBTNode firstNode = getNode(0); if(firstNode instanceof CommonBTHeaderNode) { return (CommonBTHeaderNode) firstNode; } else { throw new RuntimeException("Unexpected node type at catalog node " + "0: " + firstNode.getClass()); } } public String[] listAttributeNames(final CommonHFSCatalogNodeID nodeID) { final LinkedList<String> list = new LinkedList<String>(); listAttributeNames(nodeID, list); return list.toArray(new String[list.size()]); } public void listAttributeNames(final CommonHFSCatalogNodeID nodeID, LinkedList<String> list) { final LinkedList<CommonHFSAttributesLeafRecord> recordList = new LinkedList<CommonHFSAttributesLeafRecord>(); listAttributeRecords(nodeID, recordList); for(CommonHFSAttributesLeafRecord r : recordList) { CommonHFSAttributesKey k = r.getKey(); if(k.getStartBlock() == 0) { list.add(new String(k.getAttrName())); } } } public CommonHFSAttributesLeafRecord[] listAttributeRecords( final CommonHFSCatalogNodeID nodeID) { final LinkedList<CommonHFSAttributesLeafRecord> list = new LinkedList<CommonHFSAttributesLeafRecord>(); listAttributeRecords(nodeID, list); return list.toArray(new CommonHFSAttributesLeafRecord[list.size()]); } public void listAttributeRecords(final CommonHFSCatalogNodeID nodeID, final LinkedList<CommonHFSAttributesLeafRecord> list) { final CommonBTNode rootNode = getRootNode(); if(rootNode != null) { listAttributeRecords(rootNode, nodeID, list); } } private void listAttributeRecords(final CommonBTNode curNode, final CommonHFSCatalogNodeID nodeID, final LinkedList<CommonHFSAttributesLeafRecord> list) { final CommonHFSAttributesKey searchKey = CommonHFSAttributesKey.create(new HFSPlusAttributesKey( ((CommonHFSCatalogNodeID.HFSPlusImplementation) nodeID). getHFSCatalogNodeID(), 0, new char[0])); final CommonHFSAttributesKey endKey = CommonHFSAttributesKey.create(new HFSPlusAttributesKey( ((CommonHFSCatalogNodeID.HFSPlusImplementation) nodeID.add(1)).getHFSCatalogNodeID(), 0, new char[0])); if(curNode instanceof CommonHFSAttributesLeafNode) { CommonHFSAttributesLeafNode leafNode = (CommonHFSAttributesLeafNode) curNode; int listSizeBefore = list.size(); if(!findLEKeys(leafNode, searchKey, endKey, true, list)) { while(list.size() > listSizeBefore) { list.removeLast(); } } } else if(curNode instanceof CommonHFSAttributesIndexNode) { CommonHFSAttributesIndexNode indexNode = (CommonHFSAttributesIndexNode) curNode; LinkedList<CommonBTIndexRecord<CommonHFSAttributesKey>> recList = new LinkedList<CommonBTIndexRecord<CommonHFSAttributesKey>> (); /* Search for all keys in index node between search key (inclusive) * and end key (exclusive). */ findLEKeys(indexNode, searchKey, endKey, false, recList); for(CommonBTIndexRecord<CommonHFSAttributesKey> rec : recList) { listAttributeRecords(getNode(rec.getIndex()), nodeID, list); } } else { throw new RuntimeException("Unexpected node type: " + curNode.getClass()); } } }