public MergeResult merge(Mapping mapping, boolean simulate, boolean updateAllTypes) {
   try (ReleasableLock lock = mappingWriteLock.acquire()) {
     final MergeResult mergeResult = new MergeResult(simulate, updateAllTypes);
     this.mapping.merge(mapping, mergeResult);
     if (simulate == false) {
       addMappers(mergeResult.getNewObjectMappers(), mergeResult.getNewFieldMappers());
       refreshSource();
     }
     return mergeResult;
   }
 }
 protected void checkNewMappersCompatibility(
     Collection<ObjectMapper> newObjectMappers,
     Collection<FieldMapper> newFieldMappers,
     boolean updateAllTypes) {
   assert mappingLock.isWriteLockedByCurrentThread();
   for (ObjectMapper newObjectMapper : newObjectMappers) {
     ObjectMapper existingObjectMapper = fullPathObjectMappers.get(newObjectMapper.fullPath());
     if (existingObjectMapper != null) {
       MergeResult result = new MergeResult(true, updateAllTypes);
       existingObjectMapper.merge(newObjectMapper, result);
       if (result.hasConflicts()) {
         throw new IllegalArgumentException(
             "Mapper for ["
                 + newObjectMapper.fullPath()
                 + "] conflicts with existing mapping in other types"
                 + Arrays.toString(result.buildConflicts()));
       }
     }
   }
   fieldTypes.checkCompatibility(newFieldMappers, updateAllTypes);
 }
Example #3
0
  /**
   * Updates the index after a content merge has happened. If no conflict has occurred this includes
   * persisting the merged content to the object database. In case of conflicts this method takes
   * care to write the correct stages to the index.
   *
   * @param base
   * @param ours
   * @param theirs
   * @param result
   * @throws FileNotFoundException
   * @throws IOException
   */
  private void updateIndex(
      CanonicalTreeParser base,
      CanonicalTreeParser ours,
      CanonicalTreeParser theirs,
      MergeResult<RawText> result)
      throws FileNotFoundException, IOException {
    File mergedFile = !inCore ? writeMergedFile(result) : null;
    if (result.containsConflicts()) {
      // A conflict occurred, the file will contain conflict markers
      // the index will be populated with the three stages and the
      // workdir (if used) contains the halfway merged content.
      add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
      add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
      add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
      mergeResults.put(tw.getPathString(), result);
      return;
    }

    // No conflict occurred, the file will contain fully merged content.
    // The index will be populated with the new merged version.
    DirCacheEntry dce = new DirCacheEntry(tw.getPathString());

    // Set the mode for the new content. Fall back to REGULAR_FILE if
    // we can't merge modes of OURS and THEIRS.
    int newMode = mergeFileModes(tw.getRawMode(0), tw.getRawMode(1), tw.getRawMode(2));
    dce.setFileMode(
        newMode == FileMode.MISSING.getBits() ? FileMode.REGULAR_FILE : FileMode.fromBits(newMode));
    if (mergedFile != null) {
      long len = mergedFile.length();
      dce.setLastModified(mergedFile.lastModified());
      dce.setLength((int) len);
      InputStream is = new FileInputStream(mergedFile);
      try {
        dce.setObjectId(getObjectInserter().insert(OBJ_BLOB, len, is));
      } finally {
        is.close();
      }
    } else dce.setObjectId(insertMergeResult(result));
    builder.add(dce);
  }
Example #4
0
  /**
   * Processes one path and tries to merge. This method will do all do all trivial (not content)
   * merges and will also detect if a merge will fail. The merge will fail when one of the following
   * is true
   *
   * <ul>
   *   <li>the index entry does not match the entry in ours. When merging one branch into the
   *       current HEAD, ours will point to HEAD and theirs will point to the other branch. It is
   *       assumed that the index matches the HEAD because it will only not match HEAD if it was
   *       populated before the merge operation. But the merge commit should not accidentally
   *       contain modifications done before the merge. Check the <a href=
   *       "http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html#_3_way_merge" >git
   *       read-tree</a> documentation for further explanations.
   *   <li>A conflict was detected and the working-tree file is dirty. When a conflict is detected
   *       the content-merge algorithm will try to write a merged version into the working-tree. If
   *       the file is dirty we would override unsaved data.
   * </ul>
   *
   * @param base the common base for ours and theirs
   * @param ours the ours side of the merge. When merging a branch into the HEAD ours will point to
   *     HEAD
   * @param theirs the theirs side of the merge. When merging a branch into the current HEAD theirs
   *     will point to the branch which is merged into HEAD.
   * @param index the index entry
   * @param work the file in the working tree
   * @param ignoreConflicts see {@link ResolveMerger#mergeTrees(AbstractTreeIterator, RevTree,
   *     RevTree, boolean)}
   * @return <code>false</code> if the merge will fail because the index entry didn't match ours or
   *     the working-dir file was dirty and a conflict occurred
   * @throws MissingObjectException
   * @throws IncorrectObjectTypeException
   * @throws CorruptObjectException
   * @throws IOException
   * @since 3.5
   */
  protected boolean processEntry(
      CanonicalTreeParser base,
      CanonicalTreeParser ours,
      CanonicalTreeParser theirs,
      DirCacheBuildIterator index,
      WorkingTreeIterator work,
      boolean ignoreConflicts)
      throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException,
          IOException {
    enterSubtree = true;
    final int modeO = tw.getRawMode(T_OURS);
    final int modeT = tw.getRawMode(T_THEIRS);
    final int modeB = tw.getRawMode(T_BASE);

    if (modeO == 0 && modeT == 0 && modeB == 0)
      // File is either untracked or new, staged but uncommitted
      return true;

    if (isIndexDirty()) return false;

    DirCacheEntry ourDce = null;

    if (index == null || index.getDirCacheEntry() == null) {
      // create a fake DCE, but only if ours is valid. ours is kept only
      // in case it is valid, so a null ourDce is ok in all other cases.
      if (nonTree(modeO)) {
        ourDce = new DirCacheEntry(tw.getRawPath());
        ourDce.setObjectId(tw.getObjectId(T_OURS));
        ourDce.setFileMode(tw.getFileMode(T_OURS));
      }
    } else {
      ourDce = index.getDirCacheEntry();
    }

    if (nonTree(modeO) && nonTree(modeT) && tw.idEqual(T_OURS, T_THEIRS)) {
      // OURS and THEIRS have equal content. Check the file mode
      if (modeO == modeT) {
        // content and mode of OURS and THEIRS are equal: it doesn't
        // matter which one we choose. OURS is chosen. Since the index
        // is clean (the index matches already OURS) we can keep the existing one
        keep(ourDce);
        // no checkout needed!
        return true;
      } else {
        // same content but different mode on OURS and THEIRS.
        // Try to merge the mode and report an error if this is
        // not possible.
        int newMode = mergeFileModes(modeB, modeO, modeT);
        if (newMode != FileMode.MISSING.getBits()) {
          if (newMode == modeO)
            // ours version is preferred
            keep(ourDce);
          else {
            // the preferred version THEIRS has a different mode
            // than ours. Check it out!
            if (isWorktreeDirty(work, ourDce)) return false;
            // we know about length and lastMod only after we have written the new content.
            // This will happen later. Set these values to 0 for know.
            DirCacheEntry e = add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_0, 0, 0);
            toBeCheckedOut.put(tw.getPathString(), e);
          }
          return true;
        } else {
          // FileModes are not mergeable. We found a conflict on modes.
          // For conflicting entries we don't know lastModified and length.
          add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
          add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
          add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
          unmergedPaths.add(tw.getPathString());
          mergeResults.put(
              tw.getPathString(), new MergeResult<RawText>(Collections.<RawText>emptyList()));
        }
        return true;
      }
    }

    if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
      // THEIRS was not changed compared to BASE. All changes must be in
      // OURS. OURS is chosen. We can keep the existing entry.
      if (ourDce != null) keep(ourDce);
      // no checkout needed!
      return true;
    }

    if (modeB == modeO && tw.idEqual(T_BASE, T_OURS)) {
      // OURS was not changed compared to BASE. All changes must be in
      // THEIRS. THEIRS is chosen.

      // Check worktree before checking out THEIRS
      if (isWorktreeDirty(work, ourDce)) return false;
      if (nonTree(modeT)) {
        // we know about length and lastMod only after we have written
        // the new content.
        // This will happen later. Set these values to 0 for know.
        DirCacheEntry e = add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_0, 0, 0);
        if (e != null) toBeCheckedOut.put(tw.getPathString(), e);
        return true;
      } else {
        // we want THEIRS ... but THEIRS contains a folder or the
        // deletion of the path. Delete what's in the workingtree (the
        // workingtree is clean) but do not complain if the file is
        // already deleted locally. This complements the test in
        // isWorktreeDirty() for the same case.
        if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0) return true;
        toBeDeleted.add(tw.getPathString());
        return true;
      }
    }

    if (tw.isSubtree()) {
      // file/folder conflicts: here I want to detect only file/folder
      // conflict between ours and theirs. file/folder conflicts between
      // base/index/workingTree and something else are not relevant or
      // detected later
      if (nonTree(modeO) && !nonTree(modeT)) {
        if (nonTree(modeB)) add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
        add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
        unmergedPaths.add(tw.getPathString());
        enterSubtree = false;
        return true;
      }
      if (nonTree(modeT) && !nonTree(modeO)) {
        if (nonTree(modeB)) add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
        add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
        unmergedPaths.add(tw.getPathString());
        enterSubtree = false;
        return true;
      }

      // ours and theirs are both folders or both files (and treewalk
      // tells us we are in a subtree because of index or working-dir).
      // If they are both folders no content-merge is required - we can
      // return here.
      if (!nonTree(modeO)) return true;

      // ours and theirs are both files, just fall out of the if block
      // and do the content merge
    }

    if (nonTree(modeO) && nonTree(modeT)) {
      // Check worktree before modifying files
      if (isWorktreeDirty(work, ourDce)) return false;

      // Don't attempt to resolve submodule link conflicts
      if (isGitLink(modeO) || isGitLink(modeT)) {
        add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
        add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
        add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);
        unmergedPaths.add(tw.getPathString());
        return true;
      }

      MergeResult<RawText> result = contentMerge(base, ours, theirs);
      if (ignoreConflicts) result.setContainsConflicts(false);
      updateIndex(base, ours, theirs, result);
      if (result.containsConflicts() && !ignoreConflicts) unmergedPaths.add(tw.getPathString());
      modifiedFiles.add(tw.getPathString());
    } else if (modeO != modeT) {
      // OURS or THEIRS has been deleted
      if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS))
          || (modeT != 0 && !tw.idEqual(T_BASE, T_THEIRS)))) {

        add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, 0, 0);
        add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, 0, 0);
        DirCacheEntry e = add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, 0, 0);

        // OURS was deleted checkout THEIRS
        if (modeO == 0) {
          // Check worktree before checking out THEIRS
          if (isWorktreeDirty(work, ourDce)) return false;
          if (nonTree(modeT)) {
            if (e != null) toBeCheckedOut.put(tw.getPathString(), e);
          }
        }

        unmergedPaths.add(tw.getPathString());

        // generate a MergeResult for the deleted file
        mergeResults.put(tw.getPathString(), contentMerge(base, ours, theirs));
      }
    }
    return true;
  }
  // never expose this to the outside world, we need to reparse the doc mapper so we get fresh
  // instances of field mappers to properly remove existing doc mapper
  private DocumentMapper merge(DocumentMapper mapper, boolean updateAllTypes) {
    try (ReleasableLock lock = mappingWriteLock.acquire()) {
      if (mapper.type().length() == 0) {
        throw new InvalidTypeNameException("mapping type name is empty");
      }
      if (Version.indexCreated(indexSettings).onOrAfter(Version.V_2_0_0_beta1)
          && mapper.type().length() > 255) {
        throw new InvalidTypeNameException(
            "mapping type name ["
                + mapper.type()
                + "] is too long; limit is length 255 but was ["
                + mapper.type().length()
                + "]");
      }
      if (mapper.type().charAt(0) == '_') {
        throw new InvalidTypeNameException(
            "mapping type name [" + mapper.type() + "] can't start with '_'");
      }
      if (mapper.type().contains("#")) {
        throw new InvalidTypeNameException(
            "mapping type name [" + mapper.type() + "] should not include '#' in it");
      }
      if (mapper.type().contains(",")) {
        throw new InvalidTypeNameException(
            "mapping type name [" + mapper.type() + "] should not include ',' in it");
      }
      if (Version.indexCreated(indexSettings).onOrAfter(Version.V_2_0_0_beta1)
          && mapper.type().equals(mapper.parentFieldMapper().type())) {
        throw new IllegalArgumentException(
            "The [_parent.type] option can't point to the same type");
      }
      if (typeNameStartsWithIllegalDot(mapper)) {
        if (Version.indexCreated(indexSettings).onOrAfter(Version.V_2_0_0_beta1)) {
          throw new IllegalArgumentException(
              "mapping type name [" + mapper.type() + "] must not start with a '.'");
        } else {
          logger.warn(
              "Type [{}] starts with a '.', it is recommended not to start a type name with a '.'",
              mapper.type());
        }
      }
      // we can add new field/object mappers while the old ones are there
      // since we get new instances of those, and when we remove, we remove
      // by instance equality
      DocumentMapper oldMapper = mappers.get(mapper.type());

      if (oldMapper != null) {
        MergeResult result = oldMapper.merge(mapper.mapping(), false, updateAllTypes);
        if (result.hasConflicts()) {
          // TODO: What should we do???
          if (logger.isDebugEnabled()) {
            logger.debug(
                "merging mapping for type [{}] resulted in conflicts: [{}]",
                mapper.type(),
                Arrays.toString(result.buildConflicts()));
          }
        }
        return oldMapper;
      } else {
        List<ObjectMapper> newObjectMappers = new ArrayList<>();
        List<FieldMapper> newFieldMappers = new ArrayList<>();
        for (MetadataFieldMapper metadataMapper : mapper.mapping().metadataMappers) {
          newFieldMappers.add(metadataMapper);
        }
        MapperUtils.collect(mapper.mapping().root, newObjectMappers, newFieldMappers);
        checkNewMappersCompatibility(newObjectMappers, newFieldMappers, updateAllTypes);
        addMappers(newObjectMappers, newFieldMappers);

        for (DocumentTypeListener typeListener : typeListeners) {
          typeListener.beforeCreate(mapper);
        }
        mappers = newMapBuilder(mappers).put(mapper.type(), mapper).map();
        if (mapper.parentFieldMapper().active()) {
          ImmutableSet.Builder<String> parentTypesCopy = ImmutableSet.builder();
          parentTypesCopy.addAll(parentTypes);
          parentTypesCopy.add(mapper.parentFieldMapper().type());
          parentTypes = parentTypesCopy.build();
        }
        assert assertSerialization(mapper);
        return mapper;
      }
    }
  }
    /**
     * static method to create the object Precondition: If this object is an element, the current or
     * next start element starts this object and any intervening reader events are ignorable If this
     * object is not an element, it is a complex type and the reader is at the event just after the
     * outer start element Postcondition: If this object is an element, the reader is positioned at
     * its end element If this object is a complex type, the reader is positioned at the end element
     * of its outer element
     */
    public static MergeResult parse(javax.xml.stream.XMLStreamReader reader)
        throws java.lang.Exception {
      MergeResult object = new MergeResult();

      int event;
      java.lang.String nillableValue = null;
      java.lang.String prefix = "";
      java.lang.String namespaceuri = "";
      try {

        while (!reader.isStartElement() && !reader.isEndElement()) reader.next();

        if (reader.getAttributeValue("http://www.w3.org/2001/XMLSchema-instance", "type") != null) {
          java.lang.String fullTypeName =
              reader.getAttributeValue("http://www.w3.org/2001/XMLSchema-instance", "type");
          if (fullTypeName != null) {
            java.lang.String nsPrefix = null;
            if (fullTypeName.indexOf(":") > -1) {
              nsPrefix = fullTypeName.substring(0, fullTypeName.indexOf(":"));
            }
            nsPrefix = nsPrefix == null ? "" : nsPrefix;

            java.lang.String type = fullTypeName.substring(fullTypeName.indexOf(":") + 1);

            if (!"MergeResult".equals(type)) {
              // find namespace for the prefix
              java.lang.String nsUri = reader.getNamespaceContext().getNamespaceURI(nsPrefix);
              return (MergeResult)
                  com.rsys.ws.fault.ExtensionMapper.getTypeObject(nsUri, type, reader);
            }
          }
        }

        // Note all attributes that were handled. Used to differ normal attributes
        // from anyAttributes.
        java.util.Vector handledAttributes = new java.util.Vector();

        reader.next();

        while (!reader.isStartElement() && !reader.isEndElement()) reader.next();

        if (reader.isStartElement()
            && new javax.xml.namespace.QName("urn:ws.rsys.com", "insertCount")
                .equals(reader.getName())) {

          nillableValue =
              reader.getAttributeValue("http://www.w3.org/2001/XMLSchema-instance", "nil");
          if (!"true".equals(nillableValue) && !"1".equals(nillableValue)) {

            java.lang.String content = reader.getElementText();

            object.setInsertCount(
                org.apache.axis2.databinding.utils.ConverterUtil.convertToLong(content));

          } else {

            object.setInsertCount(java.lang.Long.MIN_VALUE);

            reader.getElementText(); // throw away text nodes if any.
          }

          reader.next();

        } // End of if for expected property start element
        else {
          // A start element we are not expecting indicates an invalid parameter was passed
          throw new org.apache.axis2.databinding.ADBException(
              "Unexpected subelement " + reader.getName());
        }

        while (!reader.isStartElement() && !reader.isEndElement()) reader.next();

        if (reader.isStartElement()
            && new javax.xml.namespace.QName("urn:ws.rsys.com", "updateCount")
                .equals(reader.getName())) {

          nillableValue =
              reader.getAttributeValue("http://www.w3.org/2001/XMLSchema-instance", "nil");
          if (!"true".equals(nillableValue) && !"1".equals(nillableValue)) {

            java.lang.String content = reader.getElementText();

            object.setUpdateCount(
                org.apache.axis2.databinding.utils.ConverterUtil.convertToLong(content));

          } else {

            object.setUpdateCount(java.lang.Long.MIN_VALUE);

            reader.getElementText(); // throw away text nodes if any.
          }

          reader.next();

        } // End of if for expected property start element
        else {
          // A start element we are not expecting indicates an invalid parameter was passed
          throw new org.apache.axis2.databinding.ADBException(
              "Unexpected subelement " + reader.getName());
        }

        while (!reader.isStartElement() && !reader.isEndElement()) reader.next();

        if (reader.isStartElement()
            && new javax.xml.namespace.QName("urn:ws.rsys.com", "rejectedCount")
                .equals(reader.getName())) {

          nillableValue =
              reader.getAttributeValue("http://www.w3.org/2001/XMLSchema-instance", "nil");
          if (!"true".equals(nillableValue) && !"1".equals(nillableValue)) {

            java.lang.String content = reader.getElementText();

            object.setRejectedCount(
                org.apache.axis2.databinding.utils.ConverterUtil.convertToLong(content));

          } else {

            object.setRejectedCount(java.lang.Long.MIN_VALUE);

            reader.getElementText(); // throw away text nodes if any.
          }

          reader.next();

        } // End of if for expected property start element
        else {
          // A start element we are not expecting indicates an invalid parameter was passed
          throw new org.apache.axis2.databinding.ADBException(
              "Unexpected subelement " + reader.getName());
        }

        while (!reader.isStartElement() && !reader.isEndElement()) reader.next();

        if (reader.isStartElement()
            && new javax.xml.namespace.QName("urn:ws.rsys.com", "totalCount")
                .equals(reader.getName())) {

          nillableValue =
              reader.getAttributeValue("http://www.w3.org/2001/XMLSchema-instance", "nil");
          if (!"true".equals(nillableValue) && !"1".equals(nillableValue)) {

            java.lang.String content = reader.getElementText();

            object.setTotalCount(
                org.apache.axis2.databinding.utils.ConverterUtil.convertToLong(content));

          } else {

            object.setTotalCount(java.lang.Long.MIN_VALUE);

            reader.getElementText(); // throw away text nodes if any.
          }

          reader.next();

        } // End of if for expected property start element
        else {
          // A start element we are not expecting indicates an invalid parameter was passed
          throw new org.apache.axis2.databinding.ADBException(
              "Unexpected subelement " + reader.getName());
        }

        while (!reader.isStartElement() && !reader.isEndElement()) reader.next();

        if (reader.isStartElement()
            && new javax.xml.namespace.QName("urn:ws.rsys.com", "errorMessage")
                .equals(reader.getName())) {

          nillableValue =
              reader.getAttributeValue("http://www.w3.org/2001/XMLSchema-instance", "nil");
          if (!"true".equals(nillableValue) && !"1".equals(nillableValue)) {

            java.lang.String content = reader.getElementText();

            object.setErrorMessage(
                org.apache.axis2.databinding.utils.ConverterUtil.convertToString(content));

          } else {

            reader.getElementText(); // throw away text nodes if any.
          }

          reader.next();

        } // End of if for expected property start element
        else {
          // A start element we are not expecting indicates an invalid parameter was passed
          throw new org.apache.axis2.databinding.ADBException(
              "Unexpected subelement " + reader.getName());
        }

        while (!reader.isStartElement() && !reader.isEndElement()) reader.next();

        if (reader.isStartElement())
          // A start element we are not expecting indicates a trailing invalid property
          throw new org.apache.axis2.databinding.ADBException(
              "Unexpected subelement " + reader.getName());

      } catch (javax.xml.stream.XMLStreamException e) {
        throw new java.lang.Exception(e);
      }

      return object;
    }