사용자가 조회를 선택하고 실행할 수 있도록 ExecuteQuery.run이 호출됩니다. 이 예제에서는 ExecuteQuerty.run 메소드가 ViewRecord.Viewer라는 ExeuteQuerty.Viewer의 실제 인스턴스와 함께 제공됩니다. 이것은 눌렀을 때 Viewer.view 메소드를 호출하는 열기 단추가 결과 세트 화면에 나타나게 합니다. Viewer.view 메소드(아래에 정의됨)는 결과 세트의 선택된 행과 연관된 자원의 프록시를 전달합니다. 프록시는 CqRowData.getRecord() 메소드로 얻어집니다.
따라서 이 예제의 내용은 Viewer.view 메소드 안에 구현됩니다.
static class Viewer implements ExecuteQuery.Viewer { Viewer(CqProvider provider) { m_provider = provider; } /** * @see com.ibm.rational.stp.client.samples.ExecuteQuery.Viewer#view(com.ibm.rational.wvcm.stp.cq.CqRecord) */ public JFrame view(CqRecord record) { if (record != null) try { record = (CqRecord)record.doReadProperties(RECORD_PROPERTIES); return showRecord("View: ", record, null); } catch (WvcmException ex){ ex.printStackTrace(); } return null; } /** * 텍스트 창에 첨부 자원의 컨텐츠를 표시합니다. * * @param attachment 표시할 첨부의 첨부 프록시 * */ public void view(CqAttachment attachment) { if (attachment != null) try{ File file = File.createTempFile("attach", "tmp"); attachment.doReadContent(file.getAbsolutePath(), null); BrowserDataModel.showFile(attachment.getDisplayName(), file); } catch(Throwable ex) { Utilities.exception(null, "View Attachment", ex); } } /** * 테이블에서 레코드 자원의 ALL_FIELD_VALUES 특성. * 테이블의 열은 {@link #fieldMetaProperties} 배열의 * 컨텐츠에 의해 결정됩니다. 대부분의 오브젝트의 표시는 * 해당 오브젝트의 toString() 메소드로 구현됩니다. * * @param title 테이블을 포함하는 창의 제목 문자열 * @param record 표시할 레코드의 레코드 프록시. * define the ALL_FIELD_VALUES property and the FieldValues * in that property must define the meta-properties listed in * the {@link #fieldMetaProperties} array. * @param future 등록 정보 테이블과 함께 표시할 추가 창 컴포넌트. * (이 예제에서는 확장에 의해 사용됨). * @return 이 메소드로 작성된 GUI 컴포넌트를 포함하는 * RecordFrame 구조. * @throws WvcmException */ RecordFrame showRecord(String title, CqRecord record, JComponent[] future) throws WvcmException { final StpProperty.List<CqFieldValue<?>> fields = record.getAllFieldValues(); // 테이블 모델 정의. 각 행은 레코드 자원의 특성이며 // 각 열은 특성에 대한 메타 특성입니다. // 예: 이름, 유형 및 값. TableModel dataModel = new AbstractTableModel() { private static final long serialVersionUID = 1L; public int getColumnCount() { return fieldMetaProperties.length; } public int getRowCount() { return fields.size();} public Object getValueAt(int row, int col) { try { Object val = fields.get(row) .getMetaProperty((MetaPropertyName<?>) fieldMetaProperties[col].getRoot()); if (val instanceof CqRecord) return ((CqRecord)val).getUserFriendlyLocation() .getName(); else if (val instanceof CqAttachmentFolder) return ((CqAttachmentFolder)val) .getAttachmentList().size() + " attachments"; else return val; } catch(Throwable ex) { if (ex instanceof StpException) { return ((StpException)ex).getStpReasonCode(); } else { String name = ex.getClass().getName(); return name.substring(name.lastIndexOf(".")+1); } } } public String getColumnName(int col) { return fieldMetaProperties[col].getRoot().getName(); } }; // 디스플레이 레이아웃 정의 final JTable table = new JTable(dataModel); final JPanel panel = new JPanel(new BorderLayout()); final JPanel buttons = new JPanel(new FlowLayout()); final JButton button = new JButton("View"); final RecordFrame frame = new RecordFrame(title + record.getUserFriendlyLocation().toString(), table, fields); // 선택한 레코드 또는 첨부 필드 표시를 위해 단추 추가 buttons.add(button, BorderLayout.SOUTH); button.setEnabled(false); button.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { int[] selected = table.getSelectedRows(); for (int i =0; i < selected.length; ++i) { int row = selected[i]; if (isAttachmentList(fields, row)) { view(selectAttachment(frame, fields, row, "View")); } else { view(getRecordReferencedAt(fields, row)); } } } }); // 단추 추가(추후 예제에서 사용) if (future != null) for(int i = 0; i < future.length; ++i) buttons.add(future[i]); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // 레코드 값 필드 또는 첨부 필드를 선택한 경우에만 // 선택 변경사항을 알려주고 보기 단추를 사용할 수 있도록 요청 ListSelectionModel rowSM = table.getSelectionModel(); rowSM.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()){ int[] selected = table.getSelectedRows(); button.setEnabled(false); for (int i=0; i <selected.length; ++i) if (getRecordReferencedAt(fields, selected[i]) != null || isAttachmentList(fields, selected[i])) { button.setEnabled(true); break; } } } }); panel.add(new JScrollPane(table), BorderLayout.CENTER); panel.add(buttons, BorderLayout.SOUTH); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setContentPane(panel); frame.setBounds(g_windowX += 10, g_windowY += 10, 600, 300); frame.setVisible(true); return frame; } protected CqProvider m_provider; } /** * 첨부에 대한 특정 추가 정보 등을 포함하여 * 각 레코드 필드 값으로부터 요청된 특성 */ static final PropertyRequest VALUE_PROPERTIES = new PropertyRequest(StpResource.USER_FRIENDLY_LOCATION, CqAttachmentFolder.ATTACHMENT_LIST .nest(CqAttachment.DISPLAY_NAME, CqAttachment.FILE_NAME, CqAttachment.FILE_SIZE, CqAttachment.DESCRIPTION)); /** 요청 및 표시할 필드 메타 특성 */ static final NestedPropertyName[] fieldMetaProperties = new PropertyRequest(CqFieldValue.NAME, CqFieldValue.REQUIREDNESS, CqFieldValue.TYPE, CqFieldValue.VALUE.nest(VALUE_PROPERTIES)).toArray(); /** * 이 뷰어에 표시할 레코드로부터 데이터를 읽을 때 사용할 PropertyRequest * ALL_FIELD_VALUES 특성의 메타 특성이 아닌 * ALL_FIELD_VALUES 목록 필드의 * 메타 특성 요청을 위해 사용되는 비반향 레벨임. */ final static PropertyRequest RECORD_PROPERTIES = new PropertyRequest(CqRecord.USER_FRIENDLY_LOCATION, CqRecord.ALL_FIELD_VALUES .nest(StpProperty.VALUE.nest(fieldMetaProperties))); /** * 레코드를 참조하는 경우, 필드 특성 값을 검토하고 * 참조된 레코드에 프록시를 리턴합니다. 그렇지 않은 경우, 널(null)을 리턴합니다. * @param fields 검토할 Property.List * @param row 검토할 목록의 요소 색인. * @return 필드가 레코드를 참조하는 경우 레코드 프록시. 그렇지 않은 경우, 널(null) */ static CqRecord getRecordReferencedAt(StpProperty.List<CqFieldValue<?>> fields, int row) { try { CqFieldValue field = fields.get(row); if (field.getFieldType() == ValueType.RESOURCE && field.getValue() instanceof CqRecord) return (CqRecord)field.getValue(); } catch (WvcmException ex) { ex.printStackTrace(); } return null; } /** * 표시된 필드가 첨부 필드인지 아닌지 여부. * @param fields 검토할 Property.List * @param row 검토할 목록의 요소 색인. * @return true 주어진 색인에서 필드가 첨부 필드인 경우 */ static boolean isAttachmentList(StpProperty.List<CqFieldValue<?>> fields, int row) { if (row >= 0) try { CqFieldValue field = fields.get(row); return field.getFieldType() == ValueType.ATTACHMENT_LIST; } catch (WvcmException ex) { ex.printStackTrace(); } return false; } /** * 레코드의 지정된 필드에 연관된 첨부 목록을 사용자에게 표시하고 * 사용자가 선택할 수 있도록 허용합니다. * * @param frame 이 메소드에 의해 생성되는 대화 상자의 상위 프레임. * @param fields 검토할 Property.List * @param row 검토할 목록의 요소 색인. * @param op 선택한 첨부에서 수행되는 * 오퍼레이션을 식별하는 문자열. * @return 선택한 첨부에 대한 첨부 프록시. * 사용자가 선택하지 않은 경우 널(null). */ static CqAttachment selectAttachment(Component frame, StpProperty.List<CqFieldValue<?>> fields, int row, String op) { CqFieldValue field = fields.get(row); try { CqAttachmentFolder folder = (CqAttachmentFolder)field.getValue(); ResourceList<CqAttachment> attachments = setUserFriendlyLocation (folder.doReadProperties(ATTACHMENT_PROPERTIES) .getProperty(CqAttachmentFolder.ATTACHMENT_LIST)); if (attachments.size() > 0) { CqAttachment attachment = (CqAttachment) JOptionPane .showInputDialog(frame, "Choose an Attachment to " + op, op + " Attachment", JOptionPane.INFORMATION_MESSAGE, null, attachments.toArray(), attachments.get(0)); return attachment; } } catch(Throwable t) { Utilities.exception(frame, op + " Attachment", t);} return null; } /** * {@link #selectAttachment}에 의해 생성되는 * 첨부 선택 목록에 표시되는 첨부 특성. */ final static PropertyRequest ATTACHMENT_PROPERTIES = new PropertyRequest(CqAttachmentFolder.ATTACHMENT_LIST .nest(CqAttachment.DISPLAY_NAME, CqAttachment.FILE_NAME, CqAttachment.FILE_SIZE, CqAttachment.DESCRIPTION, CqAttachment.USER_FRIENDLY_LOCATION)); /** * ViewRecord 예제의 기본 프로그램. * ClearQuest 레코드 필드를 표시하는 뷰어 버전을 전달하여 * Provider를 인스턴스화하고 ExecuteQuery 예제를 호출함. * @param args 사용 안함. */ public static void main(String[] args) { try { CqProvider provider = Utilities.getProvider().cqProvider(); ExecuteQuery.run("View Record", provider, new Viewer(provider)); } catch(Throwable ex) { Utilities.exception(null, "View Record", ex); System.exit(0); } } /** * 레코드 필드 디스플레이의 JFrame 확장. * 테이블에 표시되는 필드 목록 및 프레임의 * JTable 컴포넌트를 클라이언트에 표시. */ static class RecordFrame extends JFrame { RecordFrame(String title, JTable table, StpProperty.List fields) { super(title); m_table = table; m_fields = fields; } JTable m_table; StpProperty.List m_fields; private static final long serialVersionUID = 1L; } /** 표시할 다음 창의 X 오프셋 */ private static int g_windowX = 200; /** 표시할 다음 창의 Y 오프셋 */ private static int g_windowY = 200;
이 예제에서 ViewRecord.Viewer는 조회에서 리턴한 레코드 표시 뿐만 아니라 레코드의 선택된 필드가 참조한 레코드를 보고 레코드 필드에 첨부된 파일을 보는데에도 사용됩니다. 사용자는 이 기능을 사용하여 한 레코드에서 다른 레코드로의 참조를 볼 수 있습니다.
ViewRecord.view(CqRecord)는 데이터베이스에 있는 레코드의 모든 필드와 뷰어에서 사용하는 각 필드의 메타 특성을 읽고 채워진 프록시를 showRecord 메소드에 전달합니다. 뷰어는 ClearQuest® 레코드의 ALL_FIELD_VALUES 특성을 사용하여 레코드의 모든 필드 목록을 가져옵니다. 각 필드에 대한 NAME, REQUIREDNESS, TYPE 및 VALUE 메타 특성이 요청됩니다. ALL_FIELD_VALUES 특성 자체가 아닌 ALL_FIELD_VALUES 특성 값에 대한 메타 특성이 얻어지도록 이러한 메타 특성 요청은 다른 VALUE 메타 특성 요청 안에 중첩된다는 점에 유의하십시오. (RECORD_PROPERTIES, VALUE_PROPERTIES 및 fieldMetaProperties 선언 참조.)
필드가 첨부 필드인 경우 값의 ATTACHMENT_LIST 특성도 요청됩니다. (필드가 첨부 필드가 아닌 경우, 이 특성 요청은 실패하지만, 이 특성은 필드가 첨부인 경우에만 액세스되므로 이러한 실패가 예외로 이어지지는 않습니다.)
ViewRecord.showRecord는 ExecuteQuery.showResults와 동일한 Swing GUI 컴포넌트 및 구조를 사용하지만 테이블의 행과 열의 내용이 다릅니다. 이 경우 각 행은 CqFieldValue 오브젝트로 표현되는 레코드의 필드입니다. 각 열은 필드의 메타 특성입니다. 일반 StpProperty.getMetaProperty 인터페이스는 각 필드의 CqFieldValue/StpProperty 구조에서 각 메타 특성 값을 페치하는데 사용됩니다. 두 가지 예외로 인해 각 메타 특성 값의 toString() 메소드는 레코드 보기의 메타 특성의 이미지 생성에 따라 달라집니다. 레코드 자원의 경우 출력의 혼잡을 줄이기 위해 USER_FRIENDLY_LOCATION 특성의 이름 필드만 표시됩니다. 첨부 필드의 경우 각 첨부 이름이 아닌 첨부 수만 표시됩니다.
필드 유형 RESOURCE_LIST, JOURNAL, STRING_LIST, STRING의 표시에도 특별한 주의를 기울여야 할 수 있습니다. 이것은 독자의 몫으로 남겨둡니다.
첨부 필드가 선택되고 보기 단추가 눌려지면 selectAttachment가 호출되고 해당 결과가 ViewRecord.view(CqAttachment)로 전달됩니다. selectAttachment 메소드는 JOptionPane.showInputDialog를 다시 사용하여 선택된 필드와 연관된 첨부 목록을 사용자에게 제시합니다. 첨부 필드 값은 첨부 폴더 자원입니다. 필드와 연관된 첨부는 해당 첨부 폴더의 제한된 구성원입니다.
ViewRecord.view(CqAttachment)는 CqAttachment.doReadContent를 사용하여 데이터베이스의 첨부 파일을 임시 파일로 읽은 후 유틸리티 메소드(모두 Swing 코드)를 호출하여 사용자에게 파일을 표시합니다.
ViewRecord.Viewer는 별도의 창에 RESOURCE_LIST 값을 표시하는 것도 지원해야 합니다. 그러나 이것도 독자의 몫으로 남겨둡니다.