     params[paramPort] = input.getData paramPort
   return params
 
+handleInputCollation = (data, config, input, port, idx) ->
+  return if not config.group and not config.field
+  # Note: the collation mechanism as shown below is not a
+  # very nice way to deal with inputs as it messes with
+  # input buffer order. Much better to handle collation
+  # in a specialized component or to separate flows by
+  # scope.
+  if config.field
+    buf = input.ports[port].getBuffer input.scope, idx
+    console.log port, config.field, data[config.field]
+    if data[config.field] is undefined
+      # First port being read, set as the field value
+      datas = buf.filter (ip) -> ip.type is 'data'
+      data[config.field] = datas[0].data[config.field]
+      # We don't need to reorder the first buffer by collation
+      return
+    # Move matching IP packet to be first in buffer
+    matchingAtIdx = null
+    for ip, idx in buf
+      continue unless ip.type is 'data'
+      continue unless ip.data[config.field] is data[config.field]
+      matchingAtIdx = idx
+      break
+    # No need to reorder if matching packet is already first
+    return if matchingAtIdx is 0
+    datas = buf.splice matchingAtIdx, 1
+    buf.unshift datas[0]
+
 getInputData = (config, input) ->
   data = {}
   for port in config.inPorts

    return outProxy[ports[0]]
  return outProxy

checkWirePatternPreconditions = (component, config, input, output) ->
  # First check for required params
  paramsMet = true
  inputsMet = true
  droppedPackets = false
  for param in config.params
    continue unless input.ports[param].isRequired()
    if input.ports[param].isAddressable()
      attached = input.attached param
      unless attached.length
        paramsMet = false
        continue
      withData = attached.filter (idx) -> input.hasData [param, idx]
      if config.arrayPolicy.params is 'all'
        paramsMet = false unless withData.length is attached.length
        continue
      paramsMet = false unless withData.length
      continue
    paramsMet = false unless input.hasData param
  # Then check actual input ports
  for port in config.inPorts
    if input.ports[port].isAddressable()
      attached = input.attached port
      unless attached.length
        inputsMet = false
        continue
      withData = attached.filter (idx) -> input.hasData [port, idx]
      if config.arrayPolicy['in'] is 'all'
        inputsMet = false unless withData.length is attached.length
        continue
      inputsMet = false unless withData.length
      continue
    unless input.hasData port
      inputsMet = false
      continue
    # If input port has data but param requirements are not
    # met, and we're in dropInput mode, read the data and
    # call done
    if config.dropInput and not paramsMet
      input.getData port
      droppedPackets = true

  output.done() if droppedPackets and not paramsMet
  return inputsMet and paramsMet

# Wraps OutPort in WirePattern to add transparent scope support
class OutPortWrapper

  # Create the processing function
  component.process (input, output, context) ->
    # Abort unless WirePattern-style preconditions don't match
    return unless checkWirePatternPreconditions config, input, output
    # Populate component.params from control ports
    component.params = populateParams config, input
    # Read input data
