public INodeAdapterFactory copy() {
    PropagatingAdapterFactory clonedInstance =
        new PropagatingAdapterFactoryImpl(getAdapterKey(), isShouldRegisterAdapter());
    // clone this adapters specific list of adapter factories too
    if (fContributedFactories != null) {

      Iterator iterator = fContributedFactories.iterator();
      clonedInstance.setContributedFactories(new ArrayList());
      while (iterator.hasNext()) {
        INodeAdapterFactory existingFactory = (INodeAdapterFactory) iterator.next();
        clonedInstance.addContributedFactories(existingFactory.copy());
      }
    }
    return clonedInstance;
  }
  /**
   * This validate call is for the ISourceValidator partial document validation approach
   *
   * @param dirtyRegion
   * @param helper
   * @param reporter
   * @see org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator
   */
  public void validate(IRegion dirtyRegion, IValidationContext helper, IReporter reporter) {
    if (helper == null || fDocument == null || !fEnableSourceValidation) return;

    if ((reporter != null) && (reporter.isCancelled() == true)) {
      throw new OperationCanceledException();
    }

    IStructuredModel model =
        StructuredModelManager.getModelManager().getExistingModelForRead(fDocument);
    if (model == null) return; // error

    try {

      IDOMDocument document = null;
      if (model instanceof IDOMModel) {
        document = ((IDOMModel) model).getDocument();
      }

      if (document == null || !hasHTMLFeature(document)) return; // ignore

      ITextFileBuffer fb = FileBufferModelManager.getInstance().getBuffer(fDocument);
      if (fb == null) return;

      IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(fb.getLocation());
      if (file == null || !file.exists()) return;

      // this will be the wrong region if it's Text (instead of Element)
      // we don't know how to validate Text
      IndexedRegion ir =
          getCoveringNode(dirtyRegion); // model.getIndexedRegion(dirtyRegion.getOffset());
      if (ir instanceof Text) {
        while (ir != null && ir instanceof Text) {
          // it's assumed that this gets the IndexedRegion to
          // the right of the end offset
          ir = model.getIndexedRegion(ir.getEndOffset());
        }
      }

      if (ir instanceof INodeNotifier) {

        INodeAdapterFactory factory = HTMLValidationAdapterFactory.getInstance();
        ValidationAdapter adapter = (ValidationAdapter) factory.adapt((INodeNotifier) ir);
        if (adapter == null) return; // error

        if (reporter != null) {
          HTMLValidationReporter rep = null;
          rep = getReporter(reporter, file, (IDOMModel) model);
          rep.clear();
          adapter.setReporter(rep);

          Message mess =
              new LocalizedMessage(
                  IMessage.LOW_SEVERITY, file.getFullPath().toString().substring(1));
          reporter.displaySubtask(this, mess);
        }
        adapter.validate(ir);
      }
    } finally {
      if (model != null) model.releaseFromRead();
    }
  }