пятница, 20 июля 2012 г.

Чтение файла SolidWorks


В продолжение предыдущих статей про SolidWorks...
P.S.:  В предыдущих статьях я описывал как можно просмотреть иерархию файла и пытался извлечь дерево материалов изделия. Для получения дерева материалов я использовал неверный подход.

Задача: прочитать фаил сохранений SolidWorks и получить информацию с описанием изделия и деревом материалов.

Для реализации я использую
Исходный код примеров можно получить/просмотреть тут GitHub


Данный пост я разобью на 3 шага:
  1. Чтение файла, поиск доступных данных
  2. Получение необходимых данных
  3. Получение дерева материалов в формате XML


Шаг 1. Чтение файла, поиск доступных данных
Для чтение файла я использую такой код
FileInputStream fis = new FileInputStream(new File(path));
POIFSFileSystem poifsFileSystem = new POIFSFileSystem(fis);
fis.close();
return poifsFileSystem.getRoot();
В результате чего я получаю Root каталог внутренней структуры файла, как правило это объект DirectoryNode. В нутрии фаил содержит иерархию в которой хранится информация по изделию
Дерево в внутри файла состоит из DirectoryNode и DocumentNode объектов.

После получения корневого элемента, нам необходимо просмотреть иерархую, дабы узнать где искать необходимою информацию. Прочитать иерархию можно с помощью данного кода:
public void readNodeAndWriteToConsoleContains(DirectoryNode node) {
        Iterator iterator = node.getEntries();
        readIterator(iterator);
    }
private void readIterator(Iterator iterator) {
        while (iterator.hasNext()) {
            readNode(iterator.next());
        }
    }
private void readNode(Entry node) {
        if (node.isDocumentEntry()) {
            System.out.println(" == Directory name: " + node.getParent().getName() + " -- Document: " + node.getName());
        }
        if (node.isDirectoryEntry()) {
            System.out.println("");
            System.out.println(" == Directory name: " + node.getName());
            readIterator(((DirectoryNode) node).getEntries());
            System.out.println("");
        }
    }


В результате работы кода мы получим что то подобное:
 == Directory name: Root Entry -- Document: Config-0-Properties
 == Directory name: Root Entry -- Document: ISolidWorksInformation
 == Directory name: Root Entry -- Document: DocumentSummaryInformation
 == Directory name: swXmlContents
 == Directory name: swXmlContents -- Document: MATERIALTREE
 == Directory name: swXmlContents -- Document: KeyWords__ZLB
 == Directory name: swXmlContents -- Document: Tables__ZLB
Как правило вся вспомогательная информация по изделию размещается в  DocumentSummaryInformation  и SummaryInformation. 


Шаг 2. Получение необходимых данных
Для начала нам необходимо получить основную информацию, в моем случае это:
  • Наименование;
  • Обозначение;
  • Инвентарный номер документа.

Поля подобные SummaryInformation в нутрии себя хранят лист PropertySet в котором есть вся необходимая информация.
PropertySet summaryInformation = readDocumentNode.read(poifsFileSystem,                   DocumentSummaryInformation.DEFAULT_STREAM_NAME);
в нутрии метод read выглядит так:
public PropertySet read(POIFSFileSystem poifsFileSystem, String nameDocument) throws IOException {
        PropertySet ps = null;
        DocumentInputStream dis = poifsFileSystem.createDocumentInputStream(nameDocument);
        try {
            ps = new PropertySet(dis);
        } catch (NoPropertySetStreamException e) {
            e.printStackTrace();
        } catch (MarkUnsupportedException e) {
            e.printStackTrace();
        } finally {
            dis.close();
        }
        return ps;
    }
читаем PropertySet:
public List read(PropertySet propertySet, String docName) {
        List result = new ArrayList();
        PropertyEntry propertyEntry;
        for (Section section : propertySet.getSections()) {
            Map objects = section.getDictionary();
            Property[] properties = section.getProperties();
            Property property;
            for (int i = 0; i < objects.size(); i++) {
                property = properties[i];
                propertyEntry = new PropertyEntry();
                propertyEntry.setKey(objects.get(property.getID()));
                propertyEntry.setValue(property.getValue().toString());
                propertyEntry.setIndex(i);
                propertyEntry.setType(property.getType());
                propertyEntry.setDocumentName(docName);
                result.add(propertyEntry);
            }
        }
        return result;
    }
 
в результате чего получаем примерно следующие:
DocumentSummaryInformation / Наименование / Какойто чертеж
DocumentSummaryInformation / Обозначение / 87514567
DocumentSummaryInformation / Инвентарный номер документа / 221673
DocumentSummaryInformation / Формат / А4
... прочие данные ...  

Шаг 3. Получение дерева материалов в формате XML
Замечательно, мы получили все нужные нам данные, теперь для полноты данных, необходимо получить дерево материалов из которых состоит изделие.  Для этого мне надо в Директории файла swXmlContents, прочитать документ MATERIALTREE, данные в котором лежат в формате xml. Задачка простая, делаю это с помощью данного кода:
получаю корневой элемент дерева и перехожу в swXmlContents:
DirectoryNode swXmlContentsDir = (DirectoryNode) poifsFileSystem.getRoot().getEntry("swXmlContents");
после чего читаю документ  MATERIALTREE:
DocumentInputStream dis = readDocumentNode.getDocumentInputStream(swXmlContentsDir, "MATERIALTREE");
 SAXReader reader = new SAXReader();
 Document document = reader.read(dis);
getDocumentInputStream возвращает имплементацию InputStream из которой я могу прочитать поток данных, в моем случаем это данные в формате XML который я сразу читаю dom4j.




Заключение:

Спасибо за помощь Streamdown, пост в блоге "Необычная работа с документами solidworks".
Документация и примеры по работе с Apache POI