Coverage Report - com.ctrip.framework.apollo.portal.service.txtresolver.PropertyResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertyResolver
95%
82/86
87%
61/70
3.333
 
 1  
 package com.ctrip.framework.apollo.portal.service.txtresolver;
 2  
 
 3  
 import com.ctrip.framework.apollo.core.dto.ItemChangeSets;
 4  
 import com.ctrip.framework.apollo.core.dto.ItemDTO;
 5  
 import com.ctrip.framework.apollo.core.exception.BadRequestException;
 6  
 import com.ctrip.framework.apollo.common.utils.BeanUtils;
 7  
 
 8  
 import org.springframework.stereotype.Component;
 9  
 
 10  
 import java.util.HashMap;
 11  
 import java.util.HashSet;
 12  
 import java.util.List;
 13  
 import java.util.Map;
 14  
 import java.util.Set;
 15  
 
 16  
 /**
 17  
  * normal property file resolver.
 18  
  * update comment and blank item implement by create new item and delete old item.
 19  
  * update normal key/value item implement by update.
 20  
  */
 21  
 @Component
 22  1
 public class PropertyResolver implements ConfigTextResolver {
 23  
 
 24  
   private static final String KV_SEPARATOR = "=";
 25  
   private static final String ITEM_SEPARATOR = "\n";
 26  
 
 27  
   @Override
 28  
   public ItemChangeSets resolve(long namespaceId, String configText, List<ItemDTO> baseItems) {
 29  
 
 30  11
     Map<Integer, ItemDTO> oldLineNumMapItem = BeanUtils.mapByKey("lineNum", baseItems);
 31  11
     Map<String, ItemDTO> oldKeyMapItem = BeanUtils.mapByKey("key", baseItems);
 32  
 
 33  
     //remove comment and blank item map.
 34  11
     oldKeyMapItem.remove("");
 35  
 
 36  11
     String[] newItems = configText.split(ITEM_SEPARATOR);
 37  
 
 38  11
     if (isHasRepeatKey(newItems)){
 39  0
       throw new BadRequestException("config text has repeat key please check.");
 40  
     }
 41  
 
 42  11
     ItemChangeSets changeSets = new ItemChangeSets();
 43  11
     Map<Integer, String> newLineNumMapItem = new HashMap<Integer, String>();//use for delete blank and comment item
 44  11
     int lineCounter = 1;
 45  45
     for (String newItem : newItems) {
 46  34
       newItem = newItem.trim();
 47  34
       newLineNumMapItem.put(lineCounter, newItem);
 48  34
       ItemDTO oldItemByLine = oldLineNumMapItem.get(lineCounter);
 49  
 
 50  
       //comment item
 51  34
       if (isCommentItem(newItem)) {
 52  
 
 53  5
         handleCommentLine(namespaceId, oldItemByLine, newItem, lineCounter, changeSets);
 54  
 
 55  
         //blank item
 56  29
       } else if (isBlankItem(newItem)) {
 57  
 
 58  5
         handleBlankLine(namespaceId, oldItemByLine, lineCounter, changeSets);
 59  
 
 60  
         //normal item
 61  
       } else {
 62  24
         handleNormalLine(namespaceId, oldKeyMapItem, newItem, lineCounter, changeSets);
 63  
       }
 64  
 
 65  34
       lineCounter++;
 66  
     }
 67  
 
 68  11
     deleteCommentAndBlankItem(oldLineNumMapItem, newLineNumMapItem, changeSets);
 69  11
     deleteNormalKVItem(oldKeyMapItem, changeSets);
 70  
 
 71  11
     return changeSets;
 72  
   }
 73  
 
 74  
   private boolean isHasRepeatKey(String[] newItems){
 75  11
     Set<String> keys = new HashSet<>();
 76  11
     int lineCounter = 1;
 77  11
     int keyCount = 0;
 78  45
     for (String item: newItems){
 79  34
       if (!isCommentItem(item) && !isBlankItem(item)){
 80  24
         keyCount++;
 81  24
         String[] kv = parseKeyValueFromItem(item);
 82  24
         if (kv != null) {
 83  24
           keys.add(kv[0]);
 84  
         }else {
 85  0
           throw new BadRequestException("line:" + lineCounter + " key value must separate by '='");
 86  
         }
 87  
       }
 88  34
       lineCounter ++;
 89  
     }
 90  
 
 91  11
     return keyCount > keys.size();
 92  
   }
 93  
 
 94  
   private String[] parseKeyValueFromItem(String item){
 95  48
     int kvSeparator = item.indexOf(KV_SEPARATOR);
 96  48
     if (kvSeparator == -1) {
 97  0
       return null;
 98  
     }
 99  
 
 100  48
     String[] kv = new String[2];
 101  48
     kv[0] = item.substring(0, kvSeparator).trim();
 102  48
     kv[1] = item.substring(kvSeparator + 1, item.length()).trim();
 103  48
     return kv;
 104  
   }
 105  
 
 106  
   private void handleCommentLine(Long namespaceId, ItemDTO oldItemByLine, String newItem, int lineCounter, ItemChangeSets changeSets) {
 107  5
     String oldComment = oldItemByLine == null ? "" : oldItemByLine.getComment();
 108  
     //create comment. implement update comment by delete old comment and create new comment
 109  5
     if (!(isCommentItem(oldItemByLine) && newItem.equals(oldComment))) {
 110  4
       changeSets.addCreateItem(buildCommentItem(0l, namespaceId, newItem, lineCounter));
 111  
     }
 112  5
   }
 113  
 
 114  
   private void handleBlankLine(Long namespaceId, ItemDTO oldItem, int lineCounter, ItemChangeSets changeSets) {
 115  5
     if (!isBlankItem(oldItem)) {
 116  4
       changeSets.addCreateItem(buildBlankItem(0l, namespaceId, lineCounter));
 117  
     }
 118  5
   }
 119  
 
 120  
   private void handleNormalLine(Long namespaceId, Map<String, ItemDTO> keyMapOldItem, String newItem,
 121  
                                    int lineCounter, ItemChangeSets changeSets) {
 122  
 
 123  24
     String[] kv = parseKeyValueFromItem(newItem);
 124  
 
 125  24
     if (kv == null) {
 126  0
       throw new BadRequestException("line:" + lineCounter + " key value must separate by '='");
 127  
     }
 128  
 
 129  24
     String newKey = kv[0];
 130  24
     String newValue = kv[1].replace("\\n", "\n"); //handle user input \n
 131  
 
 132  24
     ItemDTO oldItem = keyMapOldItem.get(newKey);
 133  
 
 134  24
     if (oldItem == null) {//new item
 135  5
       changeSets.addCreateItem(buildNormalItem(0l, namespaceId, newKey, newValue, "", lineCounter));
 136  19
     } else if (!newValue.equals(oldItem.getValue()) || lineCounter != oldItem.getLineNum()){//update item
 137  30
       changeSets.addUpdateItem(
 138  15
           buildNormalItem(oldItem.getId(), namespaceId, newKey, newValue, oldItem.getComment(),
 139  
                           lineCounter));
 140  
     }
 141  24
     keyMapOldItem.remove(newKey);
 142  24
   }
 143  
 
 144  
   private boolean isCommentItem(ItemDTO item) {
 145  33
     return item != null && "".equals(item.getKey())
 146  8
            && (item.getComment().startsWith("#") || item.getComment().startsWith("!"));
 147  
   }
 148  
 
 149  
   private boolean isCommentItem(String line) {
 150  68
     return line != null && (line.startsWith("#") || line.startsWith("!"));
 151  
   }
 152  
 
 153  
   private boolean isBlankItem(ItemDTO item) {
 154  36
     return item != null && "".equals(item.getKey()) && "".equals(item.getComment());
 155  
   }
 156  
 
 157  
   private boolean isBlankItem(String line) {
 158  62
     return "".equals(line);
 159  
   }
 160  
 
 161  
   private void deleteNormalKVItem(Map<String, ItemDTO> baseKeyMapItem, ItemChangeSets changeSets) {
 162  
     //surplus item is to be deleted
 163  11
     for (Map.Entry<String, ItemDTO> entry : baseKeyMapItem.entrySet()) {
 164  4
       changeSets.addDeleteItem(entry.getValue());
 165  4
     }
 166  11
   }
 167  
 
 168  
   private void deleteCommentAndBlankItem(Map<Integer, ItemDTO> oldLineNumMapItem,
 169  
                                          Map<Integer, String> newLineNumMapItem,
 170  
                                          ItemChangeSets changeSets) {
 171  
 
 172  11
     for (Map.Entry<Integer, ItemDTO> entry : oldLineNumMapItem.entrySet()) {
 173  31
       int lineNum = entry.getKey();
 174  31
       ItemDTO oldItem = entry.getValue();
 175  31
       String newItem = newLineNumMapItem.get(lineNum);
 176  
 
 177  
       //1. old is blank by now is not
 178  
       //2.old is comment by now is not exist or modified
 179  31
       if ((isBlankItem(oldItem) && !isBlankItem(newItem))
 180  28
           || isCommentItem(oldItem) && (newItem == null || !newItem.equals(oldItem.getComment()))) {
 181  6
         changeSets.addDeleteItem(oldItem);
 182  
       }
 183  31
     }
 184  11
   }
 185  
 
 186  
   private ItemDTO buildCommentItem(Long id, Long namespaceId, String comment, int lineNum) {
 187  4
     return buildNormalItem(id, namespaceId, "", "", comment, lineNum);
 188  
   }
 189  
 
 190  
   private ItemDTO buildBlankItem(Long id, Long namespaceId, int lineNum) {
 191  4
     return buildNormalItem(id, namespaceId, "", "", "", lineNum);
 192  
   }
 193  
 
 194  
   private ItemDTO buildNormalItem(Long id, Long namespaceId, String key, String value, String comment, int lineNum) {
 195  28
     ItemDTO item = new ItemDTO(key, value, comment, lineNum);
 196  28
     item.setId(id);
 197  28
     item.setNamespaceId(namespaceId);
 198  28
     return item;
 199  
   }
 200  
 }