1d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch// Use of this source code is governed by a BSD-style license that can be 3d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch// found in the LICENSE file. 4d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch 5d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/value_extractors.h" 6d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch 73551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include "tools/gn/build_settings.h" 8d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/err.h" 9d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/label.h" 10d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/source_dir.h" 11d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/source_file.h" 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "tools/gn/target.h" 135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "tools/gn/value.h" 14d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch 15d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochnamespace { 16d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch 175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Sets the error and returns false on failure. 185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)template<typename T, class Converter> 195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool ListValueExtractor(const Value& value, 205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) std::vector<T>* dest, 215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Err* err, 225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const Converter& converter) { 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!value.VerifyTypeIs(Value::LIST, err)) 245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return false; 255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const std::vector<Value>& input_list = value.list_value(); 265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) dest->resize(input_list.size()); 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for (size_t i = 0; i < input_list.size(); i++) { 285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!converter(input_list[i], &(*dest)[i], err)) 295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return false; 305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return true; 325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Like the above version but extracts to a UniqueVector and sets the error if 355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// there are duplicates. 365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)template<typename T, class Converter> 375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool ListValueUniqueExtractor(const Value& value, 385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) UniqueVector<T>* dest, 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Err* err, 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const Converter& converter) { 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!value.VerifyTypeIs(Value::LIST, err)) 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return false; 435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const std::vector<Value>& input_list = value.list_value(); 445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for (size_t i = 0; i < input_list.size(); i++) { 465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) T new_one; 475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!converter(input_list[i], &new_one, err)) 485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return false; 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (!dest->push_back(new_one)) { 505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Already in the list, throw error. 515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) *err = Err(input_list[i], "Duplicate item in list"); 525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) size_t previous_index = dest->IndexOf(new_one); 535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) err->AppendSubErr(Err(input_list[previous_index], 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) "This was the previous definition.")); 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return false; 565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return true; 595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 61d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch// This extractor rejects files with system-absolute file paths. If we need 62d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch// that in the future, we'll have to add some flag to control this. 63d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochstruct RelativeFileConverter { 643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) RelativeFileConverter(const BuildSettings* build_settings_in, 653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) const SourceDir& current_dir_in) 663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) : build_settings(build_settings_in), 673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) current_dir(current_dir_in) { 683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) } 69d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch bool operator()(const Value& v, SourceFile* out, Err* err) const { 70d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch if (!v.VerifyTypeIs(Value::STRING, err)) 71d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return false; 723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) *out = current_dir.ResolveRelativeFile(v.string_value(), 733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) build_settings->root_path_utf8()); 74d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch if (out->is_system_absolute()) { 75d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch *err = Err(v, "System-absolute file path.", 76d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch "You can't list a system-absolute file path here. Please include " 77d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch "only files in\nthe source tree. Maybe you meant to begin with two " 78d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch "slashes to indicate an\nabsolute path in the source tree?"); 79d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return false; 80d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch } 81d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return true; 82d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch } 833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) const BuildSettings* build_settings; 84d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch const SourceDir& current_dir; 85d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}; 86d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch 87d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochstruct RelativeDirConverter { 883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) RelativeDirConverter(const BuildSettings* build_settings_in, 893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) const SourceDir& current_dir_in) 903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) : build_settings(build_settings_in), 913551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) current_dir(current_dir_in) { 923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) } 93d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch bool operator()(const Value& v, SourceDir* out, Err* err) const { 94d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch if (!v.VerifyTypeIs(Value::STRING, err)) 95d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return false; 963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) *out = current_dir.ResolveRelativeDir(v.string_value(), 973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) build_settings->root_path_utf8()); 98d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return true; 99d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch } 1003551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) const BuildSettings* build_settings; 101d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch const SourceDir& current_dir; 102d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}; 103d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch 1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Fills in a label. 1050f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)template<typename T> struct LabelResolver { 106d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch LabelResolver(const SourceDir& current_dir_in, 107d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch const Label& current_toolchain_in) 108d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch : current_dir(current_dir_in), 109d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch current_toolchain(current_toolchain_in) {} 1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bool operator()(const Value& v, Label* out, Err* err) const { 1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!v.VerifyTypeIs(Value::STRING, err)) 1121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return false; 1131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *out = Label::Resolve(current_dir, current_toolchain, v, err); 1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return !err->has_error(); 1151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const SourceDir& current_dir; 1171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const Label& current_toolchain; 1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}; 1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Fills the label part of a LabelPtrPair, leaving the pointer null. 1211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccitemplate<typename T> struct LabelPtrResolver { 1221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci LabelPtrResolver(const SourceDir& current_dir_in, 1231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const Label& current_toolchain_in) 1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci : current_dir(current_dir_in), 1251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci current_toolchain(current_toolchain_in) {} 1260f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) bool operator()(const Value& v, LabelPtrPair<T>* out, Err* err) const { 127d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch if (!v.VerifyTypeIs(Value::STRING, err)) 128d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return false; 1290f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) out->label = Label::Resolve(current_dir, current_toolchain, v, err); 1300f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) out->origin = v.origin(); 131d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return !err->has_error(); 132d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch } 133d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch const SourceDir& current_dir; 134d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch const Label& current_toolchain; 135d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}; 136d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch 137d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch} // namespace 138d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch 139d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochbool ExtractListOfStringValues(const Value& value, 140d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch std::vector<std::string>* dest, 141d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch Err* err) { 142d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch if (!value.VerifyTypeIs(Value::LIST, err)) 143d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return false; 144d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch const std::vector<Value>& input_list = value.list_value(); 145d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch dest->reserve(input_list.size()); 146d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch for (size_t i = 0; i < input_list.size(); i++) { 147d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch if (!input_list[i].VerifyTypeIs(Value::STRING, err)) 148d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return false; 149d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch dest->push_back(input_list[i].string_value()); 150d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch } 151d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return true; 152d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch} 153d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch 1543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)bool ExtractListOfRelativeFiles(const BuildSettings* build_settings, 1553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) const Value& value, 156d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch const SourceDir& current_dir, 157d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch std::vector<SourceFile>* files, 158d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch Err* err) { 159d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return ListValueExtractor(value, files, err, 1603551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) RelativeFileConverter(build_settings, current_dir)); 161d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch} 162d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch 1633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)bool ExtractListOfRelativeDirs(const BuildSettings* build_settings, 1643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) const Value& value, 165d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch const SourceDir& current_dir, 166d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch std::vector<SourceDir>* dest, 167d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch Err* err) { 168d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch return ListValueExtractor(value, dest, err, 1693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) RelativeDirConverter(build_settings, current_dir)); 170d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch} 171d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch 172d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochbool ExtractListOfLabels(const Value& value, 173d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch const SourceDir& current_dir, 174d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch const Label& current_toolchain, 1750f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) LabelTargetVector* dest, 1760f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) Err* err) { 1770f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return ListValueExtractor(value, dest, err, 1781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci LabelPtrResolver<Target>(current_dir, 1791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci current_toolchain)); 180d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch} 181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool ExtractListOfUniqueLabels(const Value& value, 1835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const SourceDir& current_dir, 1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const Label& current_toolchain, 1851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci UniqueVector<Label>* dest, 1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Err* err) { 1875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return ListValueUniqueExtractor(value, dest, err, 1885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) LabelResolver<Config>(current_dir, 1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) current_toolchain)); 1905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 1915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool ExtractListOfUniqueLabels(const Value& value, 1935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const SourceDir& current_dir, 1945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) const Label& current_toolchain, 1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci UniqueVector<LabelConfigPair>* dest, 1961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Err* err) { 1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return ListValueUniqueExtractor(value, dest, err, 1981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci LabelPtrResolver<Config>(current_dir, 1991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci current_toolchain)); 2001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool ExtractListOfUniqueLabels(const Value& value, 2031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const SourceDir& current_dir, 2041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const Label& current_toolchain, 2055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) UniqueVector<LabelTargetPair>* dest, 2065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Err* err) { 2075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return ListValueUniqueExtractor(value, dest, err, 2081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci LabelPtrResolver<Target>(current_dir, 2091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci current_toolchain)); 2105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)} 2115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 212f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool ExtractRelativeFile(const BuildSettings* build_settings, 213f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const Value& value, 214f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const SourceDir& current_dir, 215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) SourceFile* file, 216f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Err* err) { 217f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) RelativeFileConverter converter(build_settings, current_dir); 218f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return converter(value, file, err); 219f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 220