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