001 /* 002 * The contents of this file are subject to the Mozilla Public 003 * License Version 1.1 (the "License"); you may not use this file 004 * except in compliance with the License. You may obtain a copy of 005 * the License at http://www.mozilla.org/MPL/ 006 * 007 * Software distributed under the License is distributed on an "AS 008 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 009 * implied. See the License for the specific language governing 010 * rights and limitations under the License. 011 * 012 * The Original Code is Knowtator. 013 * 014 * The Initial Developer of the Original Code is University of Colorado. 015 * Copyright (C) 2005 - 2008. All Rights Reserved. 016 * 017 * Knowtator was developed by the Center for Computational Pharmacology 018 * (http://compbio.uchcs.edu) at the University of Colorado Health 019 * Sciences Center School of Medicine with support from the National 020 * Library of Medicine. 021 * 022 * Current information about Knowtator can be obtained at 023 * http://knowtator.sourceforge.net/ 024 * 025 * Contributor(s): 026 * Philip V. Ogren <philip@ogren.info> (Original Author) 027 */ 028 029 package edu.uchsc.ccp.knowtator.xml; 030 031 import java.awt.Component; 032 import java.io.File; 033 import java.io.IOException; 034 import java.util.ArrayList; 035 import java.util.Collections; 036 import java.util.Iterator; 037 import java.util.List; 038 039 import javax.swing.JFileChooser; 040 import javax.swing.JOptionPane; 041 042 import org.jdom.Attribute; 043 import org.jdom.DataConversionException; 044 import org.jdom.Document; 045 import org.jdom.Element; 046 import org.jdom.JDOMException; 047 import org.jdom.input.SAXBuilder; 048 049 import edu.stanford.smi.protege.model.Cls; 050 import edu.stanford.smi.protege.model.Frame; 051 import edu.stanford.smi.protege.model.Instance; 052 import edu.stanford.smi.protege.model.KnowledgeBase; 053 import edu.stanford.smi.protege.model.Project; 054 import edu.stanford.smi.protege.model.SimpleInstance; 055 import edu.stanford.smi.protege.model.Slot; 056 import edu.stanford.smi.protege.util.CollectionUtilities; 057 import edu.uchsc.ccp.knowtator.AnnotationUtil; 058 import edu.uchsc.ccp.knowtator.FilterUtil; 059 import edu.uchsc.ccp.knowtator.KnowtatorProjectUtil; 060 import edu.uchsc.ccp.knowtator.MentionUtil; 061 import edu.uchsc.ccp.knowtator.Span; 062 import edu.uchsc.ccp.knowtator.TextSourceUtil; 063 import edu.uchsc.ccp.knowtator.textsource.TextSource; 064 import edu.uchsc.ccp.knowtator.textsource.TextSourceAccessException; 065 066 public class XMLImport { 067 public static final String XML_IMPORT_DIRECTORY = "XML_IMPORT_FILE"; 068 069 private static File getRecentXMLImportDirectory(Project project) { 070 String path = (String) project.getClientInformation(XML_IMPORT_DIRECTORY); 071 if (path == null) 072 return null; 073 074 File xmlImportDirectory = new File(path); 075 if (xmlImportDirectory.exists() && xmlImportDirectory.isDirectory()) { 076 return xmlImportDirectory; 077 } 078 return null; 079 } 080 081 public static void readFromXML(Component parent, KnowledgeBase kb, KnowtatorProjectUtil kpu, 082 TextSourceUtil textSourceUtil, AnnotationUtil annotationUtil, MentionUtil mentionUtil, 083 FilterUtil filterUtil, Project project) { 084 085 int option = JOptionPane.showConfirmDialog(parent, 086 "The following dialogs allow you to import annotations from XML.\n" 087 + "Please consult available documentation before using this feature.\n" 088 + "Before importing annotations please save and/or archive your current\n" 089 + "Knowtator project. If an exception is thrown during annotation import,\n" 090 + "then importing will stop and an error message will appear stating the problem.\n" 091 + "Otherwise, a message stating successful completion will appear. If annotation\n" 092 + "import does not successfully complete, please discard all changes made by\n" 093 + "the partial import by closing your Knowtator project without saving changes\n" 094 + "and reopening.", "XML Import", JOptionPane.OK_CANCEL_OPTION); 095 if (option != JOptionPane.OK_OPTION) 096 return; 097 098 JFileChooser chooser = new JFileChooser(); 099 chooser.setDialogTitle("Please choose xml files to read from."); 100 chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); 101 chooser.setMultiSelectionEnabled(true); 102 103 File recentXMLImportDirectory = getRecentXMLImportDirectory(project); 104 if (recentXMLImportDirectory != null) { 105 chooser.setCurrentDirectory(recentXMLImportDirectory.getParentFile()); 106 } 107 108 int returnVal = chooser.showOpenDialog(parent); 109 if (returnVal != JFileChooser.APPROVE_OPTION) 110 return; 111 112 File[] selectedFiles = chooser.getSelectedFiles(); 113 114 System.out.println("selectedFiles.size()=" + selectedFiles.length); 115 if (selectedFiles.length > 0) { 116 project.setClientInformation(XML_IMPORT_DIRECTORY, selectedFiles[0].getParent()); 117 } 118 119 try { 120 for (File xmlFile : selectedFiles) { 121 readFromXML(xmlFile, kb, kpu, annotationUtil, filterUtil, mentionUtil, textSourceUtil); 122 } 123 } catch (KnowtatorXMLException kxe) { 124 JOptionPane.showMessageDialog(parent, kxe, "Exception thrown while importing annotations", 125 JOptionPane.ERROR_MESSAGE); 126 JOptionPane.showMessageDialog(parent, "Please discard all changes made by this partial import\n" 127 + "by close this Knowtator project without saving changes and reopening", "Please discard changes", 128 JOptionPane.WARNING_MESSAGE); 129 kxe.printStackTrace(); 130 return; 131 } 132 133 JOptionPane.showMessageDialog(parent, "XML import successfully completed.\n" 134 + "To discard changes created by import close this Protege project without saving.", 135 "XML import complete", JOptionPane.INFORMATION_MESSAGE); 136 } 137 138 public static void readFromXML(File xmlFile, KnowledgeBase kb, KnowtatorProjectUtil kpu, 139 AnnotationUtil annotationUtil, FilterUtil filterUtil, MentionUtil mentionUtil, TextSourceUtil textSourceUtil) 140 throws KnowtatorXMLException { 141 try { 142 SAXBuilder builder = new SAXBuilder(); 143 Document doc = builder.build(xmlFile); 144 145 Element root = doc.getRootElement(); 146 Attribute textSourceAttribute = root.getAttribute(XMLConstants.TEXT_SOURCE_ATTRIBUTE_NAME); 147 String textSourceID = textSourceAttribute.getValue(); 148 SimpleInstance textSource = kb.getSimpleInstance(textSourceID); 149 if (textSource == null) { 150 TextSource ts = textSourceUtil.getCurrentTextSourceCollection().get(textSourceID); 151 textSource = textSourceUtil.getTextSourceInstance(ts, true); 152 } 153 154 readClsMentions(root, kb, kpu); 155 readInstanceMentions(root, kb, kpu); 156 readSlotMentions(root, kb, kpu); 157 readMentionSlots(root, kb, kpu, XMLConstants.CLASS_MENTION_ELEMENT_NAME); 158 readMentionSlots(root, kb, kpu, XMLConstants.INSTANCE_MENTION_ELEMENT_NAME); 159 readAnnotations(root, kb, kpu, annotationUtil, textSource); 160 } catch (IOException ioe) { 161 throw new KnowtatorXMLException(ioe); 162 } catch (JDOMException jde) { 163 throw new KnowtatorXMLException(jde); 164 } catch (TextSourceAccessException tsae) { 165 throw new KnowtatorXMLException(tsae); 166 } 167 } 168 169 /** 170 * This method iterates through all of the class mention elements and checks 171 * to see that a corresponding 'knowtator class mention' instance exists in 172 * the Protege project. If not, then they are created. The mentioned class 173 * is also set for the Protege instance. 174 * 175 * @param root 176 * @param kb 177 * @param kpu 178 */ 179 180 private static void readClsMentions(Element root, KnowledgeBase kb, KnowtatorProjectUtil kpu) 181 throws KnowtatorXMLException { 182 List classMentionElements = root.getChildren(XMLConstants.CLASS_MENTION_ELEMENT_NAME); 183 Iterator classMentionElementsItr = classMentionElements.iterator(); 184 185 while (classMentionElementsItr.hasNext()) { 186 Element classMentionElement = (Element) classMentionElementsItr.next(); 187 188 String classMentionID = classMentionElement.getAttribute(XMLConstants.ID_ATTRIBUTE_NAME).getValue(); 189 Instance classMentionInstance = kb.getInstance(classMentionID); 190 if (classMentionInstance == null) // create class mention if it does 191 // not already exist from the 192 // class mention id 193 { 194 classMentionInstance = kb.createSimpleInstance(null, classMentionID, CollectionUtilities 195 .createCollection(kpu.getClassMentionCls()), true); 196 } 197 198 String mentionedClassID = classMentionElement.getChild(XMLConstants.MENTION_CLASS_ELEMENT_NAME) 199 .getAttribute(XMLConstants.ID_ATTRIBUTE_NAME).getValue(); 200 Cls mentionedCls = kb.getCls(mentionedClassID); 201 if (mentionedCls == null) { 202 throw new KnowtatorXMLException("There is no class defined in Protege with the name '" 203 + mentionedClassID + "'."); 204 } 205 classMentionInstance.setOwnSlotValue(kpu.getMentionClassSlot(), mentionedCls); 206 } 207 } 208 209 private static void readInstanceMentions(Element root, KnowledgeBase kb, KnowtatorProjectUtil kpu) 210 throws KnowtatorXMLException { 211 List instanceMentionElements = root.getChildren(XMLConstants.INSTANCE_MENTION_ELEMENT_NAME); 212 Iterator instanceMentionElementsItr = instanceMentionElements.iterator(); 213 214 while (instanceMentionElementsItr.hasNext()) { 215 Element instanceMentionElement = (Element) instanceMentionElementsItr.next(); 216 217 // create instance mention if it does not already exist from the 218 // class mention id 219 String instanceMentionID = instanceMentionElement.getAttribute(XMLConstants.ID_ATTRIBUTE_NAME).getValue(); 220 Instance instanceMentionInstance = kb.getInstance(instanceMentionID); 221 if (instanceMentionInstance == null) { 222 instanceMentionInstance = kb.createSimpleInstance(null, instanceMentionID, CollectionUtilities 223 .createCollection(kpu.getInstanceMentionCls()), true); 224 } 225 226 // set the mentioned instance of the instance mention 227 String mentionedInstanceID = instanceMentionElement.getChild(XMLConstants.MENTION_INSTANCE_ELEMENT_NAME) 228 .getAttribute(XMLConstants.ID_ATTRIBUTE_NAME).getValue(); 229 Instance mentionedInstance = kb.getInstance(mentionedInstanceID); 230 if (mentionedInstance == null) { 231 throw new KnowtatorXMLException("There is no instance defined in Protege with the name '" 232 + mentionedInstanceID + "'."); 233 } 234 instanceMentionInstance.setOwnSlotValue(kpu.getMentionClassSlot(), mentionedInstance); 235 } 236 } 237 238 private static void readSlotMentions(Element root, KnowledgeBase kb, KnowtatorProjectUtil kpu) 239 throws KnowtatorXMLException { 240 241 List<Element> slotMentionElements = getSlotMentionElements(root); 242 243 for (Element slotMentionElement : slotMentionElements) { 244 String slotMentionID = slotMentionElement.getAttribute(XMLConstants.ID_ATTRIBUTE_NAME).getValue(); 245 Instance slotMentionInstance = kb.getInstance(slotMentionID); 246 // set the mentioned slot of the slot mention 247 String mentionedSlotID = slotMentionElement.getChild(XMLConstants.MENTION_SLOT_ELEMENT_NAME).getAttribute( 248 XMLConstants.ID_ATTRIBUTE_NAME).getValue(); 249 Slot mentionedSlot = kb.getSlot(mentionedSlotID); 250 if (mentionedSlot == null) { 251 throw new KnowtatorXMLException("There is no slot defined in Protege with the name '" + mentionedSlotID 252 + "'."); 253 } 254 255 if (slotMentionElement.getName().equals(XMLConstants.COMPLEX_SLOT_MENTION_ELEMENT_NAME)) { 256 if (slotMentionInstance == null) 257 slotMentionInstance = kb.createSimpleInstance(null, slotMentionID, CollectionUtilities 258 .createCollection(kpu.getComplexSlotMentionCls()), true); 259 else 260 slotMentionInstance.setOwnSlotValues(kpu.getMentionSlotValueSlot(), Collections.EMPTY_LIST); 261 readComplexSlotMentionValues(slotMentionElement, slotMentionInstance, kb, kpu); 262 } else if (slotMentionElement.getName().equals(XMLConstants.BOOLEAN_SLOT_MENTION_ELEMENT_NAME)) { 263 if (slotMentionInstance == null) 264 slotMentionInstance = kb.createSimpleInstance(null, slotMentionID, CollectionUtilities 265 .createCollection(kpu.getBooleanSlotMentionCls()), true); 266 else 267 slotMentionInstance.setOwnSlotValues(kpu.getMentionSlotValueSlot(), Collections.EMPTY_LIST); 268 readSimpleSlotMentionValues(slotMentionElement, slotMentionInstance, kb, kpu, 269 XMLConstants.BOOLEAN_SLOT_MENTION_VALUE_ELEMENT_NAME); 270 } else if (slotMentionElement.getName().equals(XMLConstants.FLOAT_SLOT_MENTION_ELEMENT_NAME)) { 271 if (slotMentionInstance == null) 272 slotMentionInstance = kb.createSimpleInstance(null, slotMentionID, CollectionUtilities 273 .createCollection(kpu.getFloatSlotMentionCls()), true); 274 else 275 slotMentionInstance.setOwnSlotValues(kpu.getMentionSlotValueSlot(), Collections.EMPTY_LIST); 276 readSimpleSlotMentionValues(slotMentionElement, slotMentionInstance, kb, kpu, 277 XMLConstants.FLOAT_SLOT_MENTION_VALUE_ELEMENT_NAME); 278 } else if (slotMentionElement.getName().equals(XMLConstants.INTEGER_SLOT_MENTION_ELEMENT_NAME)) { 279 if (slotMentionInstance == null) 280 slotMentionInstance = kb.createSimpleInstance(null, slotMentionID, CollectionUtilities 281 .createCollection(kpu.getIntegerSlotMentionCls()), true); 282 else 283 slotMentionInstance.setOwnSlotValues(kpu.getMentionSlotValueSlot(), Collections.EMPTY_LIST); 284 readSimpleSlotMentionValues(slotMentionElement, slotMentionInstance, kb, kpu, 285 XMLConstants.INTEGER_SLOT_MENTION_VALUE_ELEMENT_NAME); 286 } else if (slotMentionElement.getName().equals(XMLConstants.STRING_SLOT_MENTION_ELEMENT_NAME)) { 287 if (slotMentionInstance == null) 288 slotMentionInstance = kb.createSimpleInstance(null, slotMentionID, CollectionUtilities 289 .createCollection(kpu.getStringSlotMentionCls()), true); 290 else 291 slotMentionInstance.setOwnSlotValues(kpu.getMentionSlotValueSlot(), Collections.EMPTY_LIST); 292 readSimpleSlotMentionValues(slotMentionElement, slotMentionInstance, kb, kpu, 293 XMLConstants.STRING_SLOT_MENTION_VALUE_ELEMENT_NAME); 294 } 295 296 slotMentionInstance.setOwnSlotValue(kpu.getMentionSlotSlot(), mentionedSlot); 297 } 298 } 299 300 private static void readComplexSlotMentionValues(Element slotMentionElement, Instance slotMentionInstance, 301 KnowledgeBase kb, KnowtatorProjectUtil kpu) { 302 List complexSlotMentionValueElements = slotMentionElement 303 .getChildren(XMLConstants.COMPLEX_SLOT_MENTION_VALUE_ELEMENT_NAME); 304 Iterator complexSlotMentionValueElementsItr = complexSlotMentionValueElements.iterator(); 305 306 while (complexSlotMentionValueElementsItr.hasNext()) { 307 Element complexSlotMentionValueElement = (Element) complexSlotMentionValueElementsItr.next(); 308 String complexSlotMentionValueID = complexSlotMentionValueElement.getAttribute( 309 XMLConstants.VALUE_ATTRIBUTE_NAME).getValue(); 310 Frame complexSlotMentionValue = kb.getFrame(complexSlotMentionValueID); 311 slotMentionInstance.addOwnSlotValue(kpu.getMentionSlotValueSlot(), complexSlotMentionValue); 312 } 313 } 314 315 private static void readSimpleSlotMentionValues(Element slotMentionElement, Instance slotMentionInstance, 316 KnowledgeBase kb, KnowtatorProjectUtil kpu, String elementName) { 317 318 List simpleSlotMentionValueElements = slotMentionElement.getChildren(elementName); 319 Iterator simpleSlotMentionValueElementsItr = simpleSlotMentionValueElements.iterator(); 320 321 while (simpleSlotMentionValueElementsItr.hasNext()) { 322 Element simpleSlotMentionValueElement = (Element) simpleSlotMentionValueElementsItr.next(); 323 Attribute simpleSlotMentionValueAttribute = simpleSlotMentionValueElement 324 .getAttribute(XMLConstants.VALUE_ATTRIBUTE_NAME); 325 Object slotMentionValue = null; 326 try { 327 if (elementName.equals(XMLConstants.BOOLEAN_SLOT_MENTION_VALUE_ELEMENT_NAME)) { 328 slotMentionValue = new Boolean(simpleSlotMentionValueAttribute.getBooleanValue()); 329 } else if (elementName.equals(XMLConstants.INTEGER_SLOT_MENTION_VALUE_ELEMENT_NAME)) { 330 slotMentionValue = new Integer(simpleSlotMentionValueAttribute.getIntValue()); 331 } else if (elementName.equals(XMLConstants.STRING_SLOT_MENTION_VALUE_ELEMENT_NAME)) { 332 slotMentionValue = simpleSlotMentionValueAttribute.getValue(); 333 } else if (elementName.equals(XMLConstants.FLOAT_SLOT_MENTION_VALUE_ELEMENT_NAME)) { 334 slotMentionValue = new Float(simpleSlotMentionValueAttribute.getFloatValue()); 335 } 336 } catch (DataConversionException dce) { 337 slotMentionValue = simpleSlotMentionValueAttribute.getValue(); 338 } 339 340 slotMentionInstance.addOwnSlotValue(kpu.getMentionSlotValueSlot(), slotMentionValue); 341 } 342 } 343 344 /** 345 * This method goes back through classMention or instanceMention elements 346 * and adds the slot mentions to the class mentions. In contrast the method 347 * readSlotMentions, reads in all the slotMentions so that they are in the 348 * knowledge base when this method is run. 349 * 350 * @param root 351 * @param kb 352 * @param kpu 353 * @param elementName 354 */ 355 private static void readMentionSlots(Element root, KnowledgeBase kb, KnowtatorProjectUtil kpu, String elementName) { 356 List mentionElements = root.getChildren(elementName); 357 Iterator mentionElementsItr = mentionElements.iterator(); 358 359 while (mentionElementsItr.hasNext()) { 360 Element mentionElement = (Element) mentionElementsItr.next(); 361 Attribute mentionIDAttribute = mentionElement.getAttribute(XMLConstants.ID_ATTRIBUTE_NAME); 362 String mentionID = mentionIDAttribute.getValue(); 363 Instance mentionInstance = kb.getInstance(mentionID); 364 365 List hasSlotMentionElements = mentionElement.getChildren(XMLConstants.HAS_SLOT_MENTION_ELEMENT_NAME); 366 Iterator hasSlotMentionElementsItr = hasSlotMentionElements.iterator(); 367 while (hasSlotMentionElementsItr.hasNext()) { 368 Element hasSlotMentionElement = (Element) hasSlotMentionElementsItr.next(); 369 Attribute slotMentionIDAttribute = hasSlotMentionElement.getAttribute(XMLConstants.ID_ATTRIBUTE_NAME); 370 String slotMentionID = slotMentionIDAttribute.getValue(); 371 Instance slotMention = kb.getInstance(slotMentionID); 372 mentionInstance.addOwnSlotValue(kpu.getSlotMentionSlot(), slotMention); 373 } 374 } 375 } 376 377 private static void readAnnotations(Element root, KnowledgeBase kb, KnowtatorProjectUtil kpu, 378 AnnotationUtil annotationUtil, SimpleInstance textSourceInstance) throws KnowtatorXMLException { 379 List annotationElements = root.getChildren(XMLConstants.ANNOTATION_ELEMENT_NAME); 380 Iterator annotationElementsItr = annotationElements.iterator(); 381 382 while (annotationElementsItr.hasNext()) { 383 Element annotationElement = (Element) annotationElementsItr.next(); 384 385 SimpleInstance mentionInstance = null; 386 SimpleInstance annotatorInstance = null; 387 List<Span> spans = new ArrayList<Span>(); 388 String spannedText = null; 389 String creationDate = null; 390 391 Element mentionElement = annotationElement.getChild(XMLConstants.MENTION_ELEMENT_NAME); 392 if (mentionElement != null) { 393 String mentionID = mentionElement.getAttribute(XMLConstants.ID_ATTRIBUTE_NAME).getValue(); 394 mentionInstance = kb.getSimpleInstance(mentionID); 395 } 396 397 Element annotatorElement = annotationElement.getChild(XMLConstants.ANNOTATOR_ELEMENT_NAME); 398 if (annotatorElement != null) { 399 String annotatorID = annotatorElement.getAttribute(XMLConstants.ID_ATTRIBUTE_NAME).getValue(); 400 annotatorInstance = kb.getSimpleInstance(annotatorID); 401 } 402 403 List spanElements = annotationElement.getChildren(XMLConstants.SPAN_ELEMENT_NAME); 404 Iterator spanElementsItr = spanElements.iterator(); 405 406 try { 407 while (spanElementsItr.hasNext()) { 408 Element spanElement = (Element) spanElementsItr.next(); 409 int spanStart = spanElement.getAttribute(XMLConstants.SPAN_START_ATTRIBUTE_NAME).getIntValue(); 410 int spanEnd = spanElement.getAttribute(XMLConstants.SPAN_END_ATTRIBUTE_NAME).getIntValue(); 411 Span span = new Span(spanStart, spanEnd); 412 spans.add(span); 413 } 414 } catch (DataConversionException dce) { 415 throw new KnowtatorXMLException(dce); 416 } 417 418 Element spannedTextElement = annotationElement.getChild(XMLConstants.SPANNED_TEXT_ELEMENT_NAME); 419 if (spannedTextElement != null) 420 spannedText = spannedTextElement.getText(); 421 422 Element creationDateElement = annotationElement.getChild(XMLConstants.CREATION_DATE_ELEMENT_NAME); 423 if (creationDateElement != null) { 424 creationDate = creationDateElement.getText(); 425 } 426 427 try { 428 SimpleInstance annotation = annotationUtil.createAnnotation(mentionInstance, annotatorInstance, spans, 429 spannedText, textSourceInstance, null, creationDate); 430 Element commentElement = annotationElement.getChild(XMLConstants.COMMENT_ELEMENT_NAME); 431 if (commentElement != null) { 432 String comment = commentElement.getText(); 433 annotationUtil.setComment(annotation, comment); 434 } 435 } catch (TextSourceAccessException tsae) { 436 throw new KnowtatorXMLException(tsae); 437 } 438 } 439 } 440 441 private static List<Element> getSlotMentionElements(Element root) { 442 List<Element> slotMentionElements = new ArrayList<Element>(); 443 444 _addSlotMentionElements(root, XMLConstants.COMPLEX_SLOT_MENTION_ELEMENT_NAME, slotMentionElements); 445 _addSlotMentionElements(root, XMLConstants.BOOLEAN_SLOT_MENTION_ELEMENT_NAME, slotMentionElements); 446 _addSlotMentionElements(root, XMLConstants.FLOAT_SLOT_MENTION_ELEMENT_NAME, slotMentionElements); 447 _addSlotMentionElements(root, XMLConstants.INTEGER_SLOT_MENTION_ELEMENT_NAME, slotMentionElements); 448 _addSlotMentionElements(root, XMLConstants.STRING_SLOT_MENTION_ELEMENT_NAME, slotMentionElements); 449 return slotMentionElements; 450 } 451 452 private static void _addSlotMentionElements(Element root, String elementName, List<Element> slotMentionElements) { 453 List elements = root.getChildren(elementName); 454 for (int i = 0; i < elements.size(); i++) { 455 slotMentionElements.add((Element) elements.get(i)); 456 } 457 } 458 459 }