diff --git a/frontend/src/js/component/wb-collection-content.js b/frontend/src/js/component/wb-collection-content.js index 31f5816..49c876a 100644 --- a/frontend/src/js/component/wb-collection-content.js +++ b/frontend/src/js/component/wb-collection-content.js @@ -7,11 +7,18 @@ import makeArvadosRequest from 'make-arvados-request'; class WBCollectionContent extends Component { constructor(...args) { super(...args); - this.state.path = '.'; this.state.rows = []; this.state.manifestReader = null; } + getUrl(params) { + let res = '/collection-browse/' + + (params.uuid || this.props.uuid) + '/' + + encodeURIComponent(params.collectionPath || this.props.collectionPath) + '/' + + (params.page || this.props.page); + return res; + } + componentDidMount() { let { arvHost, arvToken } = this.props.app.state; let { uuid } = this.props; @@ -26,20 +33,52 @@ class WBCollectionContent extends Component { }); } + componentWillReceiveProps(nextProps) { + this.props = nextProps; + this.prepareRows(); + } + prepareRows() { - this.setState({}); + let { manifestReader } = this.state; + let { collectionPath } = this.props; + + //path = path.split('/'); + //path = [ '.' ].concat(path); + + let listing = manifestReader.listDirectory('.' + collectionPath); + + this.setState({ + 'rows': listing.map(item => ( + (item[0] === 'd') ? [ + ({ item[1] }/), + 'Directory', + null, + (
) + ] : [ + ({ item[1] }), + 'File', + filesize(item[2]), + (
) + ] + )) + }); } - render({}, { rows }) { + render({ collectionPath }, { rows }) { return (
- + -
); } } +WBCollectionContent.defaultProps = { + 'collectionPath': '', + 'page': 0 +}; + export default WBCollectionContent; diff --git a/frontend/src/js/misc/wb-collection-manifest.js b/frontend/src/js/misc/wb-collection-manifest.js index 704cc9d..904984f 100644 --- a/frontend/src/js/misc/wb-collection-manifest.js +++ b/frontend/src/js/misc/wb-collection-manifest.js @@ -1,17 +1,52 @@ -class CMDirectory { - constructor() { - this.directories = {}; - this.files = {}; +// +// Directory: Hash[string, [Directory, File]] +// File = [blockRefs, size] +// blockRefs: Array[blockRef] +// blockRef: [locator, position, size] +// locator: String +// position: Number +// size: Number +// + +class WBManifestReader { + constructor(manifest_text) { + this.rootDir = {}; + + if (!manifest_text) + return; + + this.parse(manifest_text); + } + + makeDir(parent, name) { + if (!(name in parent)) + parent[name] = {}; + if (parent[name] instanceof Array) + throw Error('Conflict trying to create a directory - a file with the same name already exists: ' + name); + return parent[name]; + } + + makePath(path) { + if (typeof(path) === 'string') + path = path.split('/'); + let dir = this.rootDir; + for (let i = 1; i < path.length; i++) + dir = this.makeDir(dir, path[i]); + return dir; } -} -class CMFile { - constructor() { - this.blockRefs = []; - this.size = 0; + appendFile(streamName, locators, position, size, fileName) { + let path = streamName + '/' + fileName; + path = path.split('/'); + let dir = this.makePath(path.slice(0, path.length - 1)); + if (!(fileName in dir)) + dir[fileName] = [[], 0]; + if (!(dir[fileName] instanceof Array)) + throw Error('Conflict trying to create a file - a directory with the same name already exists: ' + fileName); + this.appendReferences(dir[fileName], locators, position, size); } - append(locators, position, size) { + appendReferences(file, locators, position, size) { if (size === 0) return; @@ -30,7 +65,7 @@ class CMFile { let startBlock = used.indexOf(true); let endBlock = used.lastIndexOf(true) + 1; - console.log('startBlock: ' + startBlock + ', endBlock: ' + endBlock); + // console.log('startBlock: ' + startBlock + ', endBlock: ' + endBlock); if (startBlock === -1) return; @@ -47,53 +82,20 @@ class CMFile { runSize -= blockSize; } - this.blockRefs = this.blockRefs.concat(blockRefs); - this.size += size; - } -} - -class WBManifestReader { - constructor(manifest_text) { - this.rootDir = new CMDirectory(); - - if (!manifest_text) - return; - - this.parse(manifest_text); - } - - makeDir(parent, name) { - if (!(name in parent.directories)) - parent.directories[name] = new CMDirectory(); - return parent.directories[name]; - } - - makePath(path) { - if (typeof(path) === 'string') - path = path.split('/'); - let dir = this.rootDir; - for (let i = 1; i < path.length; i++) - dir = this.makeDir(dir, path[i]); - return dir; - } - - appendFile(streamName, locators, position, size, fileName) { - let path = streamName + '/' + fileName; - path = path.split('/'); - let dir = this.makePath(path.slice(0, path.length - 1)); - if (!(fileName in dir.files)) - dir.files[fileName] = new CMFile(); - dir.files[fileName].append(locators, position, size); + file[0] = file[0].concat(blockRefs); + file[1] += size; } parse(manifest_text) { let rx = /^[a-f0-9]{32}\+[0-9]+/; let streams = manifest_text.split('\n'); + if (!streams[streams.length - 1]) + streams = streams.slice(0, streams.length - 1); streams.map(s => { let tokens = s.split(' '); - let streamName = tokens[0]; + let streamName = this.unescapeName(tokens[0]); let n = tokens.map(t => rx.exec(t)); n = n.indexOf(null, 1); @@ -104,11 +106,61 @@ class WBManifestReader { let fileTokens = tokens.slice(n); fileTokens.map(t => { let [ position, size, fileName ] = t.split(':'); + fileName = this.unescapeName(fileName); this.appendFile(streamName, locators, Number(position), Number(size), fileName); }); }); } + + findDir(path) { + if (typeof(path) === 'string') + path = path.split('/'); + if (path[0] !== '.') + throw Error('Path must begin with a dot component'); + let dir = this.rootDir; + for (let i = 1; i < path.length; i++) { + if (!(path[i] in dir)) + throw Error('Directory not found'); + if (dir[path[i]] instanceof Array) + throw Error('Path is a file not directory'); + dir = dir[path[i]]; + } + return dir; + } + + listDirectory(path) { + let dir = this.findDir(path); + let keys = Object.keys(dir); + keys.sort(); + let subdirs = keys.filter(k => !(dir[k] instanceof Array)); + let files = keys.filter(k => (dir[k] instanceof Array)); + let res = subdirs.map(k => [ 'd', k, null ]); + res = res.concat(files.map(k => [ 'f', k, dir[k][1] ])); + return res; + } + + unescapeName(name) { + return name.replace(/(\\\\|\\040)/g, (_, $1) => ($1 === '\\\\' ? '\\' : ' ')); + } + + escapeName(name) { + return name.replace(/ /g, '\\040'); + } + /* let ids = { '\\': 1, '0': 2, '4': 3 }; + let transitions = [ + [ [0, 0], [1, ''], [0, 0], [0, 0] ], + [ [0, 0], [0, '\\'], [2, ''], [0, 0] ], + ]; + let mode = 0; + for (let i = 0; i < name.length; i++) { + let b = name[i]; + let tokenId = Number(ids[b]); + [ mode, out ] = transitions[mode][tokenId]; + if (out === 0) + out = b; + } + }*/ } export { WBManifestReader }; diff --git a/frontend/src/js/page/wb-app.js b/frontend/src/js/page/wb-app.js index ea554fb..4718dca 100644 --- a/frontend/src/js/page/wb-app.js +++ b/frontend/src/js/page/wb-app.js @@ -67,7 +67,7 @@ class WBApp extends Component { - + ); } diff --git a/frontend/src/js/page/wb-collection-browse.js b/frontend/src/js/page/wb-collection-browse.js index e238702..d92cd86 100644 --- a/frontend/src/js/page/wb-collection-browse.js +++ b/frontend/src/js/page/wb-collection-browse.js @@ -4,7 +4,7 @@ import WBArvadosCrumbs from 'wb-arvados-crumbs'; import WBCollectionContent from 'wb-collection-content'; class WBCollectionBrowse extends Component { - render({ app, uuid }, {}) { + render({ app, uuid, collectionPath }, {}) { return (
@@ -15,7 +15,8 @@ class WBCollectionBrowse extends Component { This is the collection browser for { uuid }
- + ); }